Apple base416 passphrase enc (#240)

* Move PassphraseVO to use String instead of byte[] to support Encrypt annotation

* Check for unencrypted passphrases before migrating passphrase table

---------

Co-authored-by: Marcus Sorensen <mls@apple.com>
This commit is contained in:
Marcus Sorensen 2023-03-03 08:40:46 -07:00 committed by GitHub
parent 5bca36f258
commit 2316e800ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 20 deletions

View File

@ -314,8 +314,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
newVol.setFormat(oldVol.getFormat());
if (oldVol.getPassphraseId() != null) {
PassphraseVO passphrase =_passphraseDao.persist(new PassphraseVO());
passphrase.clearPassphrase();
PassphraseVO passphrase =_passphraseDao.persist(new PassphraseVO(true));
newVol.setPassphraseId(passphrase.getId());
}
@ -1689,8 +1688,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
}
s_logger.debug("Creating passphrase for the volume: " + volume.getName());
long startTime = System.currentTimeMillis();
PassphraseVO passphrase = _passphraseDao.persist(new PassphraseVO());
passphrase.clearPassphrase();
PassphraseVO passphrase = _passphraseDao.persist(new PassphraseVO(true));
volume.setPassphraseId(passphrase.getId());
long finishTime = System.currentTimeMillis();
s_logger.debug("Creating and persisting passphrase took: " + (finishTime - startTime) + " ms for the volume: " + volume.toString());

View File

@ -2,6 +2,7 @@ package org.apache.cloudstack.secret;
import com.cloud.utils.db.Encrypt;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.commons.lang3.StringUtils;
import javax.persistence.Column;
import javax.persistence.Entity;
@ -9,6 +10,7 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
@ -24,32 +26,39 @@ public class PassphraseVO {
@Column(name = "passphrase")
@Encrypt
private byte[] passphrase;
private String passphrase;
public PassphraseVO() {
try {
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] temporary = new byte[48]; // 48 byte random passphrase buffer
this.passphrase = new byte[64]; // 48 byte random passphrase as base64 for usability
random.nextBytes(temporary);
Base64.getEncoder().encode(temporary, this.passphrase);
Arrays.fill(temporary, (byte) 0); // clear passphrase from buffer
} catch (NoSuchAlgorithmException ex ) {
throw new CloudRuntimeException("Volume encryption requested but system is missing specified algorithm to generate passphrase");
}
public PassphraseVO(boolean initialize) {
if (initialize) {
try {
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] temporary = new byte[48]; // 48 byte random passphrase buffer
random.nextBytes(temporary);
this.passphrase = Base64.getEncoder().encodeToString(temporary);
Arrays.fill(temporary, (byte) 0); // clear passphrase from buffer
} catch (NoSuchAlgorithmException ex ) {
throw new CloudRuntimeException("Volume encryption requested but system is missing specified algorithm to generate passphrase");
}
}
}
public PassphraseVO(PassphraseVO existing) {
this.passphrase = existing.getPassphrase();
this.passphrase = existing.getPassphraseString();
}
public void clearPassphrase() {
if (this.passphrase != null) {
Arrays.fill(this.passphrase, (byte) 0);
public byte[] getPassphrase() {
if (StringUtils.isBlank(this.passphrase)) {
return new byte[]{};
}
return this.passphrase.getBytes();
}
public byte[] getPassphrase() { return this.passphrase; }
public String getPassphraseString() {
return this.passphrase;
}
public Long getId() { return this.id; }
}

View File

@ -486,6 +486,8 @@ public class EncryptionSecretKeyChanger {
migrateImageStoreUrlForCifs(conn);
migrateStoragePoolPathForSMB(conn);
preparePassphraseTableForMigration(conn);
// migrate columns with annotation @Encrypt
migrateEncryptedTableColumns(conn);
@ -665,6 +667,56 @@ public class EncryptionSecretKeyChanger {
System.out.println("End migrate user vm deploy_as_is details");
}
// encrypt any unencrypted passphrases using old style encryptor before we migrate
private void preparePassphraseTableForMigration(Connection conn) throws SQLException {
System.out.println("Preparing passphrase table by checking for unencrypted passphrases");
try(PreparedStatement selectPstmt = conn.prepareStatement("SELECT id, passphrase FROM passphrase");
ResultSet rs = selectPstmt.executeQuery();
PreparedStatement updatePstmt = conn.prepareStatement("UPDATE passphrase SET passphrase=? WHERE id=?")
) {
while(rs.next()) {
long id = rs.getLong(1);
String value = rs.getString(2);
if (StringUtils.isBlank(value)) {
continue;
}
// passphrases are 64 bytes long when unencrypted, longer when encrypted
if (value.length() == 64) {
// just confirm it won't decrypt, to be safe, before assuming raw value and encrypting
try {
oldEncryptor.decrypt(value);
System.out.printf("Passphrase table entry db id %d was already encrypted with old encryption\n", id);
} catch(EncryptionException | CloudRuntimeException ex) {
String message = null;
if (ex instanceof CloudRuntimeException && ex.getCause() != null) {
if ((ex.getCause() instanceof EncryptionException)) {
message = ex.getCause().getMessage();
}
} else if (ex instanceof EncryptionException) {
message = ex.getMessage();
}
if (message != null && message.contains("Failed to decrypt")) {
System.out.printf("Encrypting unencrypted passphrase table entry db id %d before migration using old encryption\n", id);
String encrypted = oldEncryptor.encrypt(value);
updatePstmt.setBytes(1, encrypted.getBytes(StandardCharsets.UTF_8));
updatePstmt.setLong(2, id);
updatePstmt.executeUpdate();
} else {
throwCloudRuntimeException("Unhandled EncryptionException", ex);
}
}
}
}
} catch (SQLException e) {
throwCloudRuntimeException("Unable to prepare passphrase table", e);
}
System.out.println("End preparing passphrase table");
}
private void migrateImageStoreUrlForCifs(Connection conn) {
System.out.println("Begin migrate image store url if protocol is cifs");

View File

@ -33,7 +33,7 @@ public class CryptSetupTest {
file.close();
String filePath = path.toAbsolutePath().toString();
PassphraseVO passphrase = new PassphraseVO();
PassphraseVO passphrase = new PassphraseVO(true);
cryptSetup.luksFormat(passphrase.getPassphrase(), CryptSetup.LuksType.LUKS, filePath);