Module: Sdkey::Crypto::Seal

Defined in:
lib/sdkey/crypto/seal.rb

Overview

Ed25519, HKDF session keys, and AES-GCM seal helpers.

Defined Under Namespace

Classes: SealedEnvelope

Class Method Summary collapse

Class Method Details

.derive_session_aes_key(client_nonce:, server_nonce:, salt_b64:, app_id:) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/sdkey/crypto/seal.rb', line 39

def derive_session_aes_key(client_nonce:, server_nonce:, salt_b64:, app_id:)
  ikm = client_nonce.b + server_nonce.b
  salt = Encoding.base64_to_bytes(salt_b64)
  info = "#{Constants::SESSION_HKDF_INFO_PREFIX}#{app_id}".b
  OpenSSL::KDF.hkdf(
    ikm,
    salt: salt,
    info: info,
    length: Constants::SESSION_AES_KEY_BYTES,
    hash: "SHA256"
  )
end

.import_public_key(public_key_b64) ⇒ Object



26
27
28
29
# File 'lib/sdkey/crypto/seal.rb', line 26

def import_public_key(public_key_b64)
  raw = Encoding.base64_to_bytes(public_key_b64)
  OpenSSL::PKey.new_raw_public_key("ED25519", raw)
end

.open_aes_gcm(aes_key, envelope) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/sdkey/crypto/seal.rb', line 67

def open_aes_gcm(aes_key, envelope)
  if envelope.is_a?(SealedEnvelope)
    iv_b64 = envelope.iv_b64
    ciphertext_b64 = envelope.ciphertext_b64
    tag_b64 = envelope.tag_b64
  else
    iv_b64 = envelope["ivB64"] || envelope[:ivB64] || envelope["iv_b64"] || envelope[:iv_b64]
    ciphertext_b64 = envelope["ciphertextB64"] || envelope[:ciphertextB64] ||
                     envelope["ciphertext_b64"] || envelope[:ciphertext_b64]
    tag_b64 = envelope["tagB64"] || envelope[:tagB64] || envelope["tag_b64"] || envelope[:tag_b64]
  end

  iv = Encoding.base64_to_bytes(iv_b64)
  ciphertext = Encoding.base64_to_bytes(ciphertext_b64)
  tag = Encoding.base64_to_bytes(tag_b64)

  cipher = OpenSSL::Cipher.new("aes-256-gcm")
  cipher.decrypt
  cipher.key = aes_key
  cipher.iv = iv
  cipher.auth_tag = tag
  cipher.update(ciphertext) + cipher.final
end

.seal_aes_gcm(aes_key, plaintext) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/sdkey/crypto/seal.rb', line 52

def seal_aes_gcm(aes_key, plaintext)
  iv = SecureRandom.random_bytes(Constants::AES_GCM_IV_BYTES)
  cipher = OpenSSL::Cipher.new("aes-256-gcm")
  cipher.encrypt
  cipher.key = aes_key
  cipher.iv = iv
  ciphertext = cipher.update(plaintext.b) + cipher.final
  tag = cipher.auth_tag(Constants::AES_GCM_TAG_BYTES)
  SealedEnvelope.new(
    iv_b64: Encoding.bytes_to_base64(iv),
    ciphertext_b64: Encoding.bytes_to_base64(ciphertext),
    tag_b64: Encoding.bytes_to_base64(tag)
  )
end

.verify_signature(public_key, payload, signature_b64) ⇒ Object



31
32
33
34
35
36
37
# File 'lib/sdkey/crypto/seal.rb', line 31

def verify_signature(public_key, payload, signature_b64)
  signature = Encoding.base64_to_bytes(signature_b64)
  message = CanonicalJson.canonical_json(payload)
  public_key.verify(nil, signature, message)
rescue OpenSSL::PKey::PKeyError, OpenSSL::OpenSSLError
  false
end