Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,34 @@ error.encrypt.encrypt_no_constructor = Get constructor for encryptor failed: %1$
# EncryptUtils.getEncrypt — instantiation failed (encryptType arg)
error.encrypt.encrypt_instantiation_failed = New encryptor instance failed: %1$s

# EncryptionProviderRegistry — provider registration and lookup
error.encrypt.invalid_provider_id = Encryption provider id must not be null or empty
error.encrypt.duplicate_provider = Encryption provider id is already registered: %1$s
error.encrypt.provider_not_found = Encryption provider is not registered: %1$s
error.encrypt.provider_returned_null = Encryption provider returned null: %1$s

# EncryptParameter — use after sensitive state was destroyed
error.encrypt.parameter_destroyed = Encryption parameters have been destroyed
error.encrypt.invalid_page_body_overhead = Invalid page body overhead %1$s for encryption profile %2$s
error.encrypt.page_body_too_short = Encrypted page body size %1$s is smaller than AEAD overhead %2$s
error.encrypt.page_context_invalid_parameter = Page crypto context requires page AEAD parameters
error.encrypt.page_context_invalid_file_id = Page crypto context requires a 16-byte file crypto id
error.encrypt.page_context_invalid_sizes = Invalid page crypto context values, uncompressed size: %1$s, compressed plaintext size: %2$s, page index: %3$s
error.encrypt.page_output_size_mismatch = AEAD page output size mismatch, expected %1$s bytes but got %2$s
error.encrypt.page_plaintext_size_mismatch = AEAD page plaintext size mismatch, expected %1$s bytes but got %2$s
error.chunk.merge_page_aead_unsupported = Page-AEAD encrypted chunks must be decrypted and rewritten before merging

# FileEncryptionHeader — malformed or unsupported file encryption metadata
error.file.encryption_header_invalid_size = Invalid file encryption header size: %1$s
error.file.encryption_header_unsupported_version = Unsupported file encryption header version: %1$s
error.file.encryption_header_malformed = Malformed file encryption header
error.file.encryption_header_trailing_bytes = File encryption header contains trailing bytes
error.file.encryption_header_invalid_component_size = Invalid file encryption header component size: %1$s
error.file.encryption_header_truncated = File encryption header is truncated, expected %1$s bytes but read %2$s
error.file.encryption_header_invalid_field = Invalid file encryption header field: %1$s
error.write.encryption_header_must_precede_data = File encryption header must be written before TsFile data
error.write.encrypted_chunk_context_mismatch = Encrypted chunks can only be copied within the same file encryption context

# === compress ===

# ICompressor.getCompressor / IUnCompressor.getUnCompressor — null type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,34 @@ error.encrypt.encrypt_no_constructor = 获取 encryptor 构造函数失败: %1$s
# EncryptUtils.getEncrypt — instantiation failed (encryptType arg)
error.encrypt.encrypt_instantiation_failed = 实例化 encryptor 失败: %1$s

# EncryptionProviderRegistry — provider registration and lookup
error.encrypt.invalid_provider_id = encryption provider id 不能为空
error.encrypt.duplicate_provider = encryption provider id 已注册: %1$s
error.encrypt.provider_not_found = encryption provider 未注册: %1$s
error.encrypt.provider_returned_null = encryption provider 返回 null: %1$s

# EncryptParameter — use after sensitive state was destroyed
error.encrypt.parameter_destroyed = encryption parameters 已销毁
error.encrypt.invalid_page_body_overhead = encryption profile %2$s 的 page body 额外开销 %1$s 无效
error.encrypt.page_body_too_short = encrypted page body 大小 %1$s 小于 AEAD 额外开销 %2$s
error.encrypt.page_context_invalid_parameter = page crypto context 需要 page AEAD 参数
error.encrypt.page_context_invalid_file_id = page crypto context 需要 16 字节的 file crypto id
error.encrypt.page_context_invalid_sizes = page crypto context 值无效,uncompressed size: %1$s,compressed plaintext size: %2$s,page index: %3$s
error.encrypt.page_output_size_mismatch = AEAD page 输出大小不匹配,预期 %1$s 字节,实际 %2$s 字节
error.encrypt.page_plaintext_size_mismatch = AEAD page plaintext 大小不匹配,预期 %1$s 字节,实际 %2$s 字节
error.chunk.merge_page_aead_unsupported = Page-AEAD encrypted chunk 合并前必须先解密并重写

# FileEncryptionHeader — malformed or unsupported file encryption metadata
error.file.encryption_header_invalid_size = file encryption header 大小无效: %1$s
error.file.encryption_header_unsupported_version = 不支持的 file encryption header 版本: %1$s
error.file.encryption_header_malformed = file encryption header 格式错误
error.file.encryption_header_trailing_bytes = file encryption header 包含多余字节
error.file.encryption_header_invalid_component_size = file encryption header 组件大小无效: %1$s
error.file.encryption_header_truncated = file encryption header 已截断,预期 %1$s 字节,实际读取 %2$s 字节
error.file.encryption_header_invalid_field = file encryption header 字段无效: %1$s
error.write.encryption_header_must_precede_data = file encryption header 必须写在 TsFile data 之前
error.write.encrypted_chunk_context_mismatch = 仅允许在相同 file encryption context 中复制 encrypted chunk

# === compress ===

# ICompressor.getCompressor / IUnCompressor.getUnCompressor — null type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,83 @@
*/
package org.apache.tsfile.encrypt;

public class EncryptParameter {
import org.apache.tsfile.exception.encrypt.EncryptException;
import org.apache.tsfile.i18n.Messages;

import javax.security.auth.Destroyable;

import java.util.Arrays;

/**
* Parameters required to encrypt or decrypt one TsFile.
*
* <p>The two-argument constructor preserves the legacy encryption API. New file-level encryption
* schemes should use {@link #pageAeadBuilder()} and identify an {@link IEncryptProvider} and one of
* its immutable encryption profiles by stable ids. Runtime implementation class names and algorithm
* implementation details are deliberately not part of the persisted parameters.
*/
public class EncryptParameter implements AutoCloseable, Destroyable {

public static final int FILE_CRYPTO_ID_LENGTH = 16;

private final String type;
private final byte[] key;
private final boolean pageAead;
private final String providerId;
private final String profileId;
private final String keyId;
private final String keyVersion;
private final byte[] wrappedDataKey;
private final byte[] fileCryptoId;

private transient volatile IEncrypt fileEncrypt;
private transient volatile boolean destroyed;

public EncryptParameter(String type, byte[] key) {
this.type = type;
this.key = key;
this.pageAead = false;
this.providerId = null;
this.profileId = null;
this.keyId = null;
this.keyVersion = null;
this.wrappedDataKey = null;
this.fileCryptoId = null;
}

private EncryptParameter(Builder builder) {
this.type = null;
this.key = copy(builder.key);
this.pageAead = true;
this.providerId = builder.providerId;
this.profileId = builder.profileId;
this.keyId = builder.keyId;
this.keyVersion = builder.keyVersion;
this.wrappedDataKey = copy(builder.wrappedDataKey);
this.fileCryptoId = copy(builder.fileCryptoId);
}

public static Builder pageAeadBuilder() {
return new Builder();
}

/** Returns an independent parameter object with no initialized runtime provider state. */
public EncryptParameter copy() {
if (destroyed) {
throw new EncryptException(Messages.get("error.encrypt.parameter_destroyed"));
}
if (!isTdePageAead()) {
return new EncryptParameter(type, key);
}
return pageAeadBuilder()
.key(key)
.providerId(providerId)
.profileId(profileId)
.keyId(keyId)
.keyVersion(keyVersion)
.wrappedDataKey(wrappedDataKey)
.fileCryptoId(fileCryptoId)
.build();
}

public byte[] getKey() {
Expand All @@ -35,4 +104,146 @@ public byte[] getKey() {
public String getType() {
return type;
}

public String getProviderId() {
return providerId;
}

public String getProfileId() {
return profileId;
}

public String getKeyId() {
return keyId;
}

public String getKeyVersion() {
return keyVersion;
}

public byte[] getWrappedDataKey() {
return copy(wrappedDataKey);
}

public byte[] getFileCryptoId() {
return copy(fileCryptoId);
}

public int getPageBodyOverhead() {
int overhead = getOrCreateFileEncrypt().getPageBodyOverhead();
if (overhead <= 0) {
throw new EncryptException(
Messages.format("error.encrypt.invalid_page_body_overhead", overhead, profileId));
}
return overhead;
}

public boolean isTdePageAead() {
return pageAead;
}

IEncrypt getOrCreateFileEncrypt() {
if (destroyed) {
throw new EncryptException(Messages.get("error.encrypt.parameter_destroyed"));
}
IEncrypt current = fileEncrypt;
if (current == null) {
synchronized (this) {
if (destroyed) {
throw new EncryptException(Messages.get("error.encrypt.parameter_destroyed"));
}
current = fileEncrypt;
if (current == null) {
current = EncryptionProviderRegistry.create(this);
fileEncrypt = current;
}
}
}
return current;
}

@Override
public boolean isDestroyed() {
return destroyed;
}

@Override
public void destroy() {
IEncrypt current;
synchronized (this) {
if (destroyed) {
return;
}
destroyed = true;
current = fileEncrypt;
fileEncrypt = null;
if (key != null && isTdePageAead()) {
Arrays.fill(key, (byte) 0);
}
}
if (current != null) {
current.close();
}
}

@Override
public void close() {
destroy();
}

private static byte[] copy(byte[] value) {
return value == null ? null : Arrays.copyOf(value, value.length);
}

public static final class Builder {

private byte[] key;
private String providerId;
private String profileId;
private String keyId;
private String keyVersion;
private byte[] wrappedDataKey;
private byte[] fileCryptoId;

private Builder() {}

public Builder key(byte[] key) {
this.key = copy(key);
return this;
}

public Builder providerId(String providerId) {
this.providerId = providerId;
return this;
}

public Builder profileId(String profileId) {
this.profileId = profileId;
return this;
}

public Builder keyId(String keyId) {
this.keyId = keyId;
return this;
}

public Builder keyVersion(String keyVersion) {
this.keyVersion = keyVersion;
return this;
}

public Builder wrappedDataKey(byte[] wrappedDataKey) {
this.wrappedDataKey = copy(wrappedDataKey);
return this;
}

public Builder fileCryptoId(byte[] fileCryptoId) {
this.fileCryptoId = copy(fileCryptoId);
return this;
}

public EncryptParameter build() {
return new EncryptParameter(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ public static EncryptParameter getEncryptParameter() {

/** Get the second EncryptParameter object according to the given type and first key. */
public static EncryptParameter getEncryptParameter(EncryptParameter param) {
if (param == null) {
return new EncryptParameter("org.apache.tsfile.encrypt.UNENCRYPTED", null);
}
if (param.isTdePageAead()) {
return param;
}
return encryptParamCache.computeIfAbsent(param, EncryptUtils::generateEncryptParameter);
}

Expand Down Expand Up @@ -285,6 +291,16 @@ public static IEncrypt getEncrypt() {
return getEncrypt(TSFileDescriptor.getInstance().getConfig());
}

public static IEncrypt getEncrypt(EncryptParameter encryptParameter) {
if (encryptParameter == null) {
return getEncrypt("org.apache.tsfile.encrypt.UNENCRYPTED", null);
}
if (encryptParameter.isTdePageAead()) {
return encryptParameter.getOrCreateFileEncrypt();
}
return getEncrypt(encryptParameter.getType(), encryptParameter.getKey());
}

public static IEncrypt getEncrypt(String encryptType, byte[] dataEncryptKey) {
try {
String className = getEncryptClass(encryptType);
Expand Down
Loading
Loading