mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
Update EncryptedMultisigDescriptor.java
Replaced broken code relying on non-existent Java methods (getAuthenticationTag() and setAuthenticationTag()), causing compilation errors. Correctly implemented AES-GCM encryption and decryption: Removed manual extraction/injection of authentication tags. Encrypted blobs now properly combine IV and ciphertext (with embedded tag). Decryption validates integrity via Cipher.doFinal(). Ensured secure key derivation using SHA-256 on sorted zpubs.
This commit is contained in:
parent
d8b3672711
commit
7fae9d7d5f
1 changed files with 42 additions and 40 deletions
|
|
@ -1,61 +1,63 @@
|
|||
// Import necessary Java libraries for encoding, cryptography, and utilities
|
||||
import java.nio.charset.StandardCharsets; // For UTF-8 charset handling
|
||||
import java.security.MessageDigest; // For SHA-256 hashing
|
||||
import java.security.SecureRandom; // For generating secure random IVs
|
||||
import java.util.Arrays; // For array operations like sorting
|
||||
import java.util.Base64; // For Base64 encoding/decoding
|
||||
import javax.crypto.Cipher; // For encryption/decryption operations
|
||||
import javax.crypto.SecretKeySpec; // For creating AES keys
|
||||
import javax.crypto.spec.GCMParameterSpec; // For GCM mode parameters
|
||||
import javax.crypto.AEADBadTagException; // For handling authentication failures in GCM
|
||||
import java.nio.charset.StandardCharsets; // For UTF-8 charset handling
|
||||
import java.security.MessageDigest; // For SHA-256 hashing
|
||||
import java.security.SecureRandom; // For generating secure random IVs
|
||||
import java.util.Arrays; // For array operations like sorting
|
||||
import java.util.Base64; // For Base64 encoding/decoding
|
||||
import javax.crypto.Cipher; // For encryption/decryption operations
|
||||
import javax.crypto.SecretKeySpec; // For creating AES keys
|
||||
import javax.crypto.spec.GCMParameterSpec; // For GCM mode parameters
|
||||
import javax.crypto.AEADBadTagException; // For handling authentication failures in GCM
|
||||
|
||||
// Define the public class for encrypted multisig descriptors
|
||||
public class EncryptedMultisigDescriptor {
|
||||
|
||||
// Constants for IV and authentication tag lengths in GCM mode
|
||||
private static final int IV_LENGTH = 12; // Standard IV size for AES-GCM
|
||||
private static final int TAG_LENGTH = 16; // Standard tag size for authentication
|
||||
private static final int IV_LENGTH = 12; // Standard IV size for AES-GCM
|
||||
private static final int TAG_LENGTH = 16; // Standard tag size for authentication
|
||||
|
||||
// Method to generate a symmetric key from two zpubs by hashing their sorted concatenation
|
||||
public static byte[] getPairKey(String zpub1, String zpub2) throws Exception {
|
||||
String[] pair = {zpub1, zpub2}; // Create array of the two zpubs
|
||||
Arrays.sort(pair); // Sort to ensure consistent order regardless of input
|
||||
String concat = pair[0] + pair[1]; // Concatenate sorted zpubs
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256"); // Get SHA-256 digest instance
|
||||
return md.digest(concat.getBytes(StandardCharsets.UTF_8)); // Hash concatenation and return bytes
|
||||
String[] pair = {zpub1, zpub2}; // Create array of the two zpubs
|
||||
Arrays.sort(pair); // Sort to ensure consistent order regardless of input
|
||||
String concat = pair[0] + pair[1]; // Concatenate sorted zpubs
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256"); // Get SHA-256 digest instance
|
||||
return md.digest(concat.getBytes(StandardCharsets.UTF_8)); // Hash concatenation and return bytes
|
||||
}
|
||||
|
||||
// Method to encrypt a descriptor string using AES-GCM with the given key
|
||||
public static String encrypt(String descriptor, byte[] key) throws Exception {
|
||||
SecretKeySpec skey = new SecretKeySpec(key, "AES"); // Create AES key from byte array
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); // Get AES-GCM cipher instance
|
||||
byte[] iv = new byte[IV_LENGTH]; // Create IV array
|
||||
new SecureRandom().nextBytes(iv); // Fill IV with secure random bytes
|
||||
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH * 8, iv); // Create GCM spec with IV and tag bits
|
||||
cipher.init(Cipher.ENCRYPT_MODE, skey, spec); // Initialize cipher for encryption
|
||||
byte[] ct = cipher.doFinal(descriptor.getBytes(StandardCharsets.UTF_8)); // Encrypt descriptor to ciphertext
|
||||
byte[] tag = cipher.getAuthenticationTag(); // Get authentication tag (unused here, but typically from doFinal)
|
||||
byte[] blob = new byte[IV_LENGTH + TAG_LENGTH + ct.length]; // Create output blob array
|
||||
System.arraycopy(iv, 0, blob, 0, IV_LENGTH); // Copy IV to start of blob
|
||||
System.arraycopy(tag, 0, blob, IV_LENGTH, TAG_LENGTH); // Copy tag after IV
|
||||
System.arraycopy(ct, 0, blob, IV_LENGTH + TAG_LENGTH, ct.length); // Copy ciphertext after tag
|
||||
return Base64.getEncoder().encodeToString(blob); // Base64 encode blob and return as string
|
||||
SecretKeySpec skey = new SecretKeySpec(key, "AES"); // Create AES key from byte array
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); // Get AES-GCM cipher instance
|
||||
byte[] iv = new byte[IV_LENGTH]; // Create IV array
|
||||
new SecureRandom().nextBytes(iv); // Fill IV with secure random bytes
|
||||
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH * 8, iv); // Create GCM spec with IV and tag bits
|
||||
cipher.init(Cipher.ENCRYPT_MODE, skey, spec); // Initialize cipher for encryption
|
||||
byte[] ct = cipher.doFinal(descriptor.getBytes(StandardCharsets.UTF_8)); // Encrypt descriptor to ciphertext
|
||||
|
||||
// Build encryption blob: IV + Ciphertext (tag included in ciphertext)
|
||||
byte[] blob = new byte[IV_LENGTH + ct.length]; // IV + Ciphertext (which includes tag)
|
||||
System.arraycopy(iv, 0, blob, 0, IV_LENGTH); // Copy IV to start of blob
|
||||
System.arraycopy(ct, 0, blob, IV_LENGTH, ct.length); // Copy entire ciphertext (tag included) after IV
|
||||
|
||||
return Base64.getEncoder().encodeToString(blob); // Base64 encode blob and return as string
|
||||
}
|
||||
|
||||
// Method to decrypt an encrypted blob string using AES-GCM with the given key
|
||||
public static String decrypt(String blobStr, byte[] key) throws Exception {
|
||||
byte[] blob = Base64.getDecoder().decode(blobStr); // Decode Base64 blob to bytes
|
||||
if (blob.length < IV_LENGTH + TAG_LENGTH) throw new IllegalArgumentException(); // Check minimum length
|
||||
byte[] iv = Arrays.copyOfRange(blob, 0, IV_LENGTH); // Extract IV from blob
|
||||
byte[] tag = Arrays.copyOfRange(blob, IV_LENGTH, IV_LENGTH + TAG_LENGTH); // Extract tag
|
||||
byte[] ct = Arrays.copyOfRange(blob, IV_LENGTH + TAG_LENGTH, blob.length); // Extract ciphertext
|
||||
SecretKeySpec skey = new SecretKeySpec(key, "AES"); // Create AES key
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); // Get cipher instance
|
||||
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH * 8, iv); // Create GCM spec
|
||||
cipher.init(Cipher.DECRYPT_MODE, skey, spec); // Initialize for decryption
|
||||
cipher.setAuthenticationTag(tag); // Set expected tag for verification (GCM-specific)
|
||||
byte[] blob = Base64.getDecoder().decode(blobStr); // Decode Base64 blob to bytes
|
||||
if (blob.length < IV_LENGTH) throw new IllegalArgumentException("Invalid blob length"); // Check minimum length
|
||||
|
||||
byte[] iv = Arrays.copyOfRange(blob, 0, IV_LENGTH); // Extract IV from blob
|
||||
byte[] ct = Arrays.copyOfRange(blob, IV_LENGTH, blob.length); // Extract ciphertext (tag included)
|
||||
|
||||
SecretKeySpec skey = new SecretKeySpec(key, "AES"); // Create AES key
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); // Get cipher instance
|
||||
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH * 8, iv); // Create GCM spec
|
||||
cipher.init(Cipher.DECRYPT_MODE, skey, spec); // Initialize for decryption
|
||||
|
||||
try {
|
||||
return new String(cipher.doFinal(ct), StandardCharsets.UTF_8); // Decrypt and return string
|
||||
return new String(cipher.doFinal(ct), StandardCharsets.UTF_8); // Decrypt and return string
|
||||
} catch (AEADBadTagException e) {
|
||||
return null; // Failed decryption due to tag mismatch
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue