blob: 666733dd51e61166b8468b994496b3a3ca4a35a7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.keyring.win32;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;
import com.sun.jna.WString;
import com.sun.jna.win32.StdCallLibrary;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.modules.keyring.utils.Utils;
import org.netbeans.modules.keyring.spi.EncryptionProvider;
import org.openide.util.Utilities;
import org.openide.util.lookup.ServiceProvider;
/**
* Data protection utility for Microsoft Windows.
* XXX org.tmatesoft.svn.core.internal.util.jna.SVNWinCrypt is a possibly more robust implementation
* (though it seems to set CRYPTPROTECT_UI_FORBIDDEN which we do not necessarily want).
*/
@ServiceProvider(service=EncryptionProvider.class, position=100)
public class Win32Protect implements EncryptionProvider {
private static final Logger LOG = Logger.getLogger(Win32Protect.class.getName());
public @Override boolean enabled() {
if (!Utilities.isWindows()) {
LOG.fine("not running on Windows");
return false;
}
if (Boolean.getBoolean("netbeans.keyring.no.native")) {
LOG.fine("native keyring integration disabled");
return false;
}
try {
if (CryptLib.INSTANCE == null) {
LOG.fine("loadLibrary -> null");
return false;
}
return true;
} catch (Throwable t) {
LOG.log(Level.FINE, null, t);
return false;
}
}
public @Override String id() {
return "win32"; // NOI18N
}
public @Override byte[] encrypt(char[] cleartext) throws Exception {
byte[] cleartextB = Utils.chars2Bytes(cleartext);
CryptIntegerBlob input = new CryptIntegerBlob();
input.store(cleartextB);
Arrays.fill(cleartextB, (byte) 0);
CryptIntegerBlob output = new CryptIntegerBlob();
if (!CryptLib.INSTANCE.CryptProtectData(input, null, null, null, null, 0, output)) {
throw new Exception("CryptProtectData failed: " + Native.getLastError());
}
input.zero();
return output.load();
}
public @Override char[] decrypt(byte[] ciphertext) throws Exception {
CryptIntegerBlob input = new CryptIntegerBlob();
input.store(ciphertext);
CryptIntegerBlob output = new CryptIntegerBlob();
if (!CryptLib.INSTANCE.CryptUnprotectData(input, null, null, null, null, 0, output)) {
throw new Exception("CryptUnprotectData failed: " + Native.getLastError());
}
byte[] result = output.load();
// XXX gives CCE because not a Memory: output.zero();
char[] cleartext = Utils.bytes2Chars(result);
Arrays.fill(result, (byte) 0);
return cleartext;
}
public @Override boolean decryptionFailed() {
return false; // not much to do about it
}
public @Override void encryptionChangingCallback(Callable<Void> callback) {}
public @Override void encryptionChanged() {
assert false;
}
public @Override void freshKeyring(boolean fresh) {}
public interface CryptLib extends StdCallLibrary {
CryptLib INSTANCE = Native.load("Crypt32", CryptLib.class); // NOI18N
/** @see <a href="http://msdn.microsoft.com/en-us/library/aa380261(VS.85,printer).aspx">Reference</a> */
boolean CryptProtectData(
CryptIntegerBlob pDataIn,
WString szDataDescr,
CryptIntegerBlob pOptionalEntropy,
Pointer pvReserved,
Pointer pPromptStruct,
int dwFlags,
CryptIntegerBlob pDataOut
)/* throws LastErrorException*/;
/** @see <a href="http://msdn.microsoft.com/en-us/library/aa380882(VS.85,printer).aspx">Reference</a> */
boolean CryptUnprotectData(
CryptIntegerBlob pDataIn,
WString[] ppszDataDescr,
CryptIntegerBlob pOptionalEntropy,
Pointer pvReserved,
Pointer pPromptStruct,
int dwFlags,
CryptIntegerBlob pDataOut
)/* throws LastErrorException*/;
}
@SuppressWarnings("PublicField")
@FieldOrder({"cbData", "pbData"})
public static class CryptIntegerBlob extends Structure {
public int cbData;
public /*byte[]*/Pointer pbData;
byte[] load() {
return pbData.getByteArray(0, cbData);
// XXX how to free pbData? [Kernel32]LocalFree?
}
void store(byte[] data) {
cbData = data.length;
pbData = new Memory(data.length);
pbData.write(0, data, 0, cbData);
}
void zero() {
((Memory) pbData).clear();
}
}
}