|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.pkcs11; |
|
|
|
import java.nio.ByteBuffer; |
|
|
|
import java.security.*; |
|
import java.security.spec.AlgorithmParameterSpec; |
|
|
|
import javax.crypto.MacSpi; |
|
|
|
import sun.nio.ch.DirectBuffer; |
|
|
|
import sun.security.pkcs11.wrapper.*; |
|
import static sun.security.pkcs11.wrapper.PKCS11Constants.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class P11Mac extends MacSpi { |
|
|
|
|
|
private final Token token; |
|
|
|
|
|
private final String algorithm; |
|
|
|
|
|
private final CK_MECHANISM ckMechanism; |
|
|
|
|
|
private final int macLength; |
|
|
|
|
|
private P11Key p11Key; |
|
|
|
|
|
private Session session; |
|
|
|
|
|
private boolean initialized; |
|
|
|
|
|
private byte[] oneByte; |
|
|
|
P11Mac(Token token, String algorithm, long mechanism) |
|
throws PKCS11Exception { |
|
super(); |
|
this.token = token; |
|
this.algorithm = algorithm; |
|
Long params = null; |
|
switch ((int)mechanism) { |
|
case (int)CKM_MD5_HMAC: |
|
macLength = 16; |
|
break; |
|
case (int)CKM_SHA_1_HMAC: |
|
macLength = 20; |
|
break; |
|
case (int)CKM_SHA224_HMAC: |
|
macLength = 28; |
|
break; |
|
case (int)CKM_SHA256_HMAC: |
|
macLength = 32; |
|
break; |
|
case (int)CKM_SHA384_HMAC: |
|
macLength = 48; |
|
break; |
|
case (int)CKM_SHA512_HMAC: |
|
macLength = 64; |
|
break; |
|
case (int)CKM_SSL3_MD5_MAC: |
|
macLength = 16; |
|
params = Long.valueOf(16); |
|
break; |
|
case (int)CKM_SSL3_SHA1_MAC: |
|
macLength = 20; |
|
params = Long.valueOf(20); |
|
break; |
|
default: |
|
throw new ProviderException("Unknown mechanism: " + mechanism); |
|
} |
|
ckMechanism = new CK_MECHANISM(mechanism, params); |
|
} |
|
|
|
|
|
private void reset(boolean doCancel) { |
|
if (!initialized) { |
|
return; |
|
} |
|
initialized = false; |
|
|
|
try { |
|
if (session == null) { |
|
return; |
|
} |
|
|
|
if (doCancel && token.explicitCancel) { |
|
cancelOperation(); |
|
} |
|
} finally { |
|
p11Key.releaseKeyID(); |
|
session = token.releaseSession(session); |
|
} |
|
} |
|
|
|
private void cancelOperation() { |
|
token.ensureValid(); |
|
// cancel operation by finishing it; avoid killSession as some |
|
|
|
try { |
|
token.p11.C_SignFinal(session.id(), 0); |
|
} catch (PKCS11Exception e) { |
|
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) { |
|
// Cancel Operation may be invoked after an error on a PKCS#11 |
|
// call. If the operation inside the token was already cancelled, |
|
// do not fail here. This is part of a defensive mechanism for |
|
|
|
return; |
|
} |
|
throw new ProviderException("Cancel failed", e); |
|
} |
|
} |
|
|
|
private void ensureInitialized() throws PKCS11Exception { |
|
if (!initialized) { |
|
initialize(); |
|
} |
|
} |
|
|
|
private void initialize() throws PKCS11Exception { |
|
if (p11Key == null) { |
|
throw new ProviderException( |
|
"Operation cannot be performed without calling engineInit first"); |
|
} |
|
token.ensureValid(); |
|
long p11KeyID = p11Key.getKeyID(); |
|
try { |
|
if (session == null) { |
|
session = token.getOpSession(); |
|
} |
|
token.p11.C_SignInit(session.id(), ckMechanism, p11KeyID); |
|
} catch (PKCS11Exception e) { |
|
p11Key.releaseKeyID(); |
|
session = token.releaseSession(session); |
|
throw e; |
|
} |
|
initialized = true; |
|
} |
|
|
|
|
|
protected int engineGetMacLength() { |
|
return macLength; |
|
} |
|
|
|
|
|
protected void engineReset() { |
|
reset(true); |
|
} |
|
|
|
|
|
protected void engineInit(Key key, AlgorithmParameterSpec params) |
|
throws InvalidKeyException, InvalidAlgorithmParameterException { |
|
if (params != null) { |
|
throw new InvalidAlgorithmParameterException |
|
("Parameters not supported"); |
|
} |
|
reset(true); |
|
p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); |
|
try { |
|
initialize(); |
|
} catch (PKCS11Exception e) { |
|
throw new InvalidKeyException("init() failed", e); |
|
} |
|
} |
|
|
|
|
|
protected byte[] engineDoFinal() { |
|
try { |
|
ensureInitialized(); |
|
return token.p11.C_SignFinal(session.id(), 0); |
|
} catch (PKCS11Exception e) { |
|
// As per the PKCS#11 standard, C_SignFinal may only |
|
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or |
|
// successful calls to determine the output length. However, |
|
// these cases are handled at OpenJDK's libj2pkcs11 native |
|
// library. Thus, P11Mac::reset can be called with a 'false' |
|
|
|
throw new ProviderException("doFinal() failed", e); |
|
} finally { |
|
reset(false); |
|
} |
|
} |
|
|
|
|
|
protected void engineUpdate(byte input) { |
|
if (oneByte == null) { |
|
oneByte = new byte[1]; |
|
} |
|
oneByte[0] = input; |
|
engineUpdate(oneByte, 0, 1); |
|
} |
|
|
|
|
|
protected void engineUpdate(byte[] b, int ofs, int len) { |
|
try { |
|
ensureInitialized(); |
|
token.p11.C_SignUpdate(session.id(), 0, b, ofs, len); |
|
} catch (PKCS11Exception e) { |
|
throw new ProviderException("update() failed", e); |
|
} |
|
} |
|
|
|
|
|
protected void engineUpdate(ByteBuffer byteBuffer) { |
|
try { |
|
ensureInitialized(); |
|
int len = byteBuffer.remaining(); |
|
if (len <= 0) { |
|
return; |
|
} |
|
if (byteBuffer instanceof DirectBuffer == false) { |
|
super.engineUpdate(byteBuffer); |
|
return; |
|
} |
|
long addr = ((DirectBuffer)byteBuffer).address(); |
|
int ofs = byteBuffer.position(); |
|
token.p11.C_SignUpdate(session.id(), addr + ofs, null, 0, len); |
|
byteBuffer.position(ofs + len); |
|
} catch (PKCS11Exception e) { |
|
throw new ProviderException("update() failed", e); |
|
} |
|
} |
|
} |