Class: BSV::Primitives::SymmetricKey
- Inherits:
-
Object
- Object
- BSV::Primitives::SymmetricKey
- Defined in:
- lib/bsv/primitives/symmetric_key.rb
Overview
AES-256-GCM symmetric encryption.
Provides authenticated encryption matching the interface used by the TS, Go, and Python reference SDKs. The wire format is:
|--- 32-byte IV ---|--- ciphertext ---|--- 16-byte auth tag ---|
All three reference SDKs use a 32-byte IV (non-standard but cross-SDK compatible) and 16-byte authentication tag.
Constant Summary collapse
- IV_SIZE =
32- TAG_SIZE =
16- KEY_SIZE =
32
Class Method Summary collapse
-
.from_ecdh(private_key, public_key) ⇒ SymmetricKey
Derive a symmetric key from an ECDH shared secret.
-
.from_random ⇒ SymmetricKey
Generate a random symmetric key.
Instance Method Summary collapse
-
#decrypt(data) ⇒ String
Decrypt an AES-256-GCM encrypted message.
-
#encrypt(plaintext) ⇒ String
Encrypt a message with AES-256-GCM.
-
#initialize(key_bytes) ⇒ SymmetricKey
constructor
A new instance of SymmetricKey.
-
#to_bytes ⇒ String
Return the raw key bytes.
Constructor Details
#initialize(key_bytes) ⇒ SymmetricKey
Returns a new instance of SymmetricKey.
29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/bsv/primitives/symmetric_key.rb', line 29 def initialize(key_bytes) key_bytes = key_bytes.b raise ArgumentError, 'key must not be empty' if key_bytes.empty? raise ArgumentError, "key must be at most #{KEY_SIZE} bytes, got #{key_bytes.bytesize}" if key_bytes.bytesize > KEY_SIZE @key = if key_bytes.bytesize < KEY_SIZE ("\x00".b * (KEY_SIZE - key_bytes.bytesize)) + key_bytes else key_bytes end end |
Class Method Details
.from_ecdh(private_key, public_key) ⇒ SymmetricKey
Derive a symmetric key from an ECDH shared secret.
Computes the shared point between the two parties and uses the X-coordinate as the key material. The X-coordinate may be 31 or 32 bytes; shorter values are left-zero-padded automatically.
62 63 64 65 66 67 |
# File 'lib/bsv/primitives/symmetric_key.rb', line 62 def self.from_ecdh(private_key, public_key) shared = private_key.derive_shared_secret(public_key) # X-coordinate = bytes 1..32 of the compressed point (skip the 02/03 prefix) x_bytes = shared.compressed.byteslice(1, 32) new(x_bytes) end |
.from_random ⇒ SymmetricKey
Generate a random symmetric key.
44 45 46 |
# File 'lib/bsv/primitives/symmetric_key.rb', line 44 def self.from_random new(SecureRandom.random_bytes(KEY_SIZE)) end |
Instance Method Details
#decrypt(data) ⇒ String
Decrypt an AES-256-GCM encrypted message.
Expects the wire format: IV (32) + ciphertext + auth tag (16).
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/bsv/primitives/symmetric_key.rb', line 101 def decrypt(data) data = data.b raise ArgumentError, "ciphertext too short: #{data.bytesize} bytes (minimum #{IV_SIZE + TAG_SIZE})" if data.bytesize < IV_SIZE + TAG_SIZE iv = data.byteslice(0, IV_SIZE) tag = data.byteslice(-TAG_SIZE, TAG_SIZE) ciphertext = data.byteslice(IV_SIZE, data.bytesize - IV_SIZE - TAG_SIZE) decipher = OpenSSL::Cipher.new('aes-256-gcm') decipher.decrypt decipher.key = @key decipher.iv_len = IV_SIZE decipher.iv = iv decipher.auth_tag = tag decipher.auth_data = ''.b ciphertext.empty? ? decipher.final : decipher.update(ciphertext) + decipher.final end |
#encrypt(plaintext) ⇒ String
Encrypt a message with AES-256-GCM.
Generates a random 32-byte IV per call. Returns the concatenation of IV, ciphertext, and 16-byte authentication tag.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/bsv/primitives/symmetric_key.rb', line 76 def encrypt(plaintext) iv = SecureRandom.random_bytes(IV_SIZE) cipher = OpenSSL::Cipher.new('aes-256-gcm') cipher.encrypt cipher.key = @key cipher.iv_len = IV_SIZE cipher.iv = iv cipher.auth_data = ''.b plaintext_bytes = plaintext.b ciphertext = plaintext_bytes.empty? ? cipher.final : cipher.update(plaintext_bytes) + cipher.final tag = cipher.auth_tag(TAG_SIZE) iv + ciphertext + tag end |
#to_bytes ⇒ String
Return the raw key bytes.
123 124 125 |
# File 'lib/bsv/primitives/symmetric_key.rb', line 123 def to_bytes @key.dup end |