Module: AccessGrid::SmartTapRevealCrypto Private

Defined in:
lib/accessgrid/smart_tap_reveal_crypto.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Internal crypto helpers for the SmartTap reveal flow.

Driven by Console#reveal_smart_tap; not part of the public SDK surface. Pure stdlib — no new gem deps.

Constant Summary collapse

CURVE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'prime256v1'
HKDF_INFO =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'accessgrid-smart-tap-reveal-v1'
KEY_LEN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

32

Class Method Summary collapse

Class Method Details

.aes_gcm_decrypt(aes_key, nonce, ciphertext, tag) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



58
59
60
61
62
63
64
65
66
67
# File 'lib/accessgrid/smart_tap_reveal_crypto.rb', line 58

def self.aes_gcm_decrypt(aes_key, nonce, ciphertext, tag)
  cipher = OpenSSL::Cipher.new('aes-256-gcm').decrypt
  cipher.key = aes_key
  cipher.iv = nonce
  cipher.auth_tag = tag
  cipher.auth_data = ''
  cipher.update(ciphertext) + cipher.final
rescue OpenSSL::Cipher::CipherError
  raise DecryptError, 'AES-GCM decryption failed (auth tag verification)'
end

.decode_envelope_bytes(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



70
71
72
73
74
75
76
# File 'lib/accessgrid/smart_tap_reveal_crypto.rb', line 70

def self.decode_envelope_bytes(value)
  raise InvalidEnvelopeError, 'Envelope iv/ciphertext/tag must be base64-encoded' unless value.is_a?(String)

  Base64.strict_decode64(value)
rescue ArgumentError
  raise InvalidEnvelopeError, 'Envelope iv/ciphertext/tag must be base64-encoded'
end

.decrypt_envelope(envelope, priv) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Decrypt the encrypted_private_key envelope from the reveal endpoint.

Performs ECDH(client_priv, server_ephemeral_pub) + HKDF-SHA256 + AES-256-GCM. Must match the server-side encryption parameters exactly.

Returns:

  • (String)

    the plaintext SmartTap private key PEM.

Raises:

  • (RuntimeError)

    on missing/bad envelope or auth-tag verification failure.



33
34
35
36
37
38
39
40
41
# File 'lib/accessgrid/smart_tap_reveal_crypto.rb', line 33

def self.decrypt_envelope(envelope, priv)
  server_pub = parse_ephemeral_public_key(envelope)
  nonce = decode_envelope_bytes(envelope['iv'])
  ciphertext = decode_envelope_bytes(envelope['ciphertext'])
  tag = decode_envelope_bytes(envelope['tag'])

  aes_key = derive_aes_key(priv, server_pub)
  aes_gcm_decrypt(aes_key, nonce, ciphertext, tag)
end

.derive_aes_key(priv, server_pub) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



52
53
54
55
# File 'lib/accessgrid/smart_tap_reveal_crypto.rb', line 52

def self.derive_aes_key(priv, server_pub)
  shared_secret = priv.dh_compute_key(server_pub.public_key)
  OpenSSL::KDF.hkdf(shared_secret, salt: '', info: HKDF_INFO, length: KEY_LEN, hash: 'SHA256')
end

.generate_keypairHash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generate a fresh ephemeral P-256 keypair for a reveal call.

Returns:

  • (Hash)

    ‘OpenSSL::PKey::EC, pub_pem: String`



21
22
23
24
# File 'lib/accessgrid/smart_tap_reveal_crypto.rb', line 21

def self.generate_keypair
  priv = OpenSSL::PKey::EC.generate(CURVE)
  { priv: priv, pub_pem: priv.public_to_pem }
end

.parse_ephemeral_public_key(envelope) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



44
45
46
47
48
49
# File 'lib/accessgrid/smart_tap_reveal_crypto.rb', line 44

def self.parse_ephemeral_public_key(envelope)
  pem = envelope['ephemeral_public_key']
  raise InvalidEnvelopeError, 'Invalid ephemeral_public_key in envelope' unless pem.is_a?(String) && !pem.empty?

  OpenSSL::PKey::EC.new(pem)
end