Skip to content

sylph01/hpke-rb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hpke-rb

Gem Version

Hybrid Public Key Encryption (HPKE; RFC 9180) in Ruby

Note

This is tested against test vectors supplied by the authors of the RFC, but is not formally audited for security. Please be aware of this when using in production.

Breaking Changes in Version 1.0.0(-rc1)

Previously, the instantiation of HPKE instance took four arguments, the curve for DHKEM, the hash function for DHKEM's HKDF, the hash function for the HKDF of HPKE, and the AEAD algorithm:

hpke = HPKE.new(:x25519, :sha256, :sha256, :aes_128_gcm)

From 1.0.0, the instantiation of the HPKE instance will be done by passing algorithm identifiers (specified in the RFC, in Section 7.1, 7.2, and 7.3):

hpke = HPKE.new(HPKE::DHKEM_X25519_HKDF_SHA256, HPKE::HKDF_SHA256, HPKE::AES_128_GCM)

# also equivalent to:
hpke = HPKE.new(0x0020, 0x0001, 0x0001)

The name of constants (and its values) are listed in the next section.

Supported Features

Supports all modes, KEMs, AEAD functions in RFC 9180.

  • HPKE Modes
    • Base
    • PSK
    • Auth
    • AuthPSK
  • Key Encapsulation Mechanisms (KEMs)
    • DHKEM(P-256, HKDF-SHA256) DHKEM_P256_HKDF_SHA256 (= 0x0010)
    • DHKEM(P-384, HKDF-SHA384) DHKEM_P384_HKDF_SHA384 (= 0x0011)
    • DHKEM(P-521, HKDF-SHA512) DHKEM_P521_HKDF_SHA512 (= 0x0012)
    • DHKEM(X25519, HKDF-SHA256) DHKEM_X25519_HKDF_SHA256 (= 0x0020)
    • DHKEM(X448, HKDF-SHA512) DHKEM_X448_HKDF_SHA512 (= 0x0021)
  • Key Derivation Functions (KDFs)
    • HKDF-SHA256 HKDF_SHA256 (= 0x0001)
    • HKDF-SHA384 HKDF_SHA384 (= 0x0002)
    • HKDF-SHA512 HKDF_SHA512 (= 0x0003)
  • AEAD Functions
    • AES-128-GCM AES_128_GCM (= 0x0001)
    • AES-256-GCM AES_256_GCM (= 0x0002)
    • ChaCha20-Poly1305 CHACHA20_POLY1305 (= 0x0003)
    • Export Only EXPORT_ONLY(= 0xffff)

Supported Environments

  • OpenSSL 3.0 or higher
    • This is due to the changes in instantiation of public/private key pairs from OpenSSL 1.1 series to OpenSSL 3.0 series
  • Ruby 3.1 or higher
    • Ruby 3.1 comes with OpenSSL 3.0 support

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add hpke

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install hpke

Usage

(example shows Base mode)

# instantiate HPKE suite
# first parameter takes the KEM ID specified in RFC 9180 Section 7.1 Table 2,
# second parameter takes the KDF ID specified in RFC 9180 Section 7.2 Table 3,
# third parameter takes the AEAD ID specified in RFC 9180 Section 7.3 Table 5.

# we will generate a different instance just for demonstration to show that nothing secret is stored in the HPKE suite instance
hpke_s = HPKE.new(HPKE::DHKEM_X25519_HKDF_SHA256, HPKE::HKDF_SHA256, HPKE::AES_128_GCM)
hpke_r = HPKE.new(HPKE::DHKEM_X25519_HKDF_SHA256, HPKE::HKDF_SHA256, HPKE::AES_128_GCM)

# get a OpenSSL::PKey::PKey instance by either generating a key or loading a key from a PEM
# see https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/PKey/PKey.html
# on the sender's end
sender_key_pair = OpenSSL::PKey.generate_key('X25519')
receiver_key_pair = OpenSSL::PKey.generate_key('X25519')

# Sender setup
# Sender knows the receiver's public key (in PEM format, in most cases), so load that into a PKey
receiver_public_key = OpenSSL::PKey.read(receiver_key_pair.public_to_pem)
encap_result = hpke_s.setup_base_s(receiver_public_key, 'info')
# This returns a hash where :enc key contains the key encapsulation,
# and :context_s contains a HPKE::ContextS instance, which is used for encryption later on.
context_s = encap_result[:context_s]
# Note that :enc contains raw bytes, so when passing to the receiver, it is advised to pass the encapsulation using Base64-encoded values
enc_base64 = Base64.encode64(encap_result[:enc])

# Then on the receiver's end
# decode the encapsulated value
enc = Base64.decode64(enc_base64)
# then use that value to generate a HPKE::ContextR instance to use for decryption
context_r = hpke_r.setup_base_r(enc, receiver_key_pair, 'info')

# sender encrypts a message
# note that the "sequence number" is incremented each time `seal` and `open` is used
ciphertext = context_s.seal('authentication_associated_data', 'plaintext')
# this is also in raw bytes, so when sending, encoding with Base64 is advised

# then receiver decrypts the ciphertext
context_r.open('authentication_associated_data', ciphertext)

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/sylph01/hpke-rb.

License

The gem is available as open source under the terms of the MIT License.

About

Hybrid Public Key Encryption (HPKE; RFC 9180) in Ruby

Resources

License

Stars

Watchers

Forks

Packages

No packages published