Class: HPKE

Inherits:
Object
  • Object
show all
Includes:
Util
Defined in:
lib/hpke.rb,
lib/hpke/version.rb

Defined Under Namespace

Modules: Util Classes: Context, ContextR, ContextS, DHKEM, HKDF

Constant Summary collapse

DHKEM_P256_HKDF_SHA256 =

Algorithm Identifiers DHKEM RFC 9180, Section 7.1 Table 2

0x0010
DHKEM_P384_HKDF_SHA384 =
0x0011
DHKEM_P521_HKDF_SHA512 =
0x0012
DHKEM_X25519_HKDF_SHA256 =
0x0020
DHKEM_X448_HKDF_SHA512 =
0x0021
AVAILABLE_KEM =
[
  DHKEM_P256_HKDF_SHA256, DHKEM_P384_HKDF_SHA384, DHKEM_P521_HKDF_SHA512, DHKEM_X25519_HKDF_SHA256, DHKEM_X448_HKDF_SHA512
]
HKDF_SHA256 =

HKDF RFC 9180, Section 7.2, Table 3

0x0001
HKDF_SHA384 =
0x0002
HKDF_SHA512 =
0x0003
AVAILABLE_KDF =
[
  HKDF_SHA256, HKDF_SHA384, HKDF_SHA512
]
AES_128_GCM =

AEAD RFC 9180, Section 7.3, Table 5

0x0001
AES_256_GCM =
0x0002
CHACHA20_POLY1305 =
0x0003
EXPORT_ONLY =
0xffff
AVAILABLE_AEAD =
[
  AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305, EXPORT_ONLY
]
CIPHERS =
{
  AES_128_GCM => {
    name: 'aes-128-gcm',
    n_k: 16,
    n_n: 12,
    n_t: 16
  },
  AES_256_GCM => {
    name: 'aes-256-gcm',
    n_k: 32,
    n_n: 12,
    n_t: 16
  },
  CHACHA20_POLY1305 => {
    name: 'chacha20-poly1305',
    n_k: 32,
    n_n: 12,
    n_t: 16
  },
  EXPORT_ONLY => {
  }
}
MODES =
{
  base: 0x00,
  psk: 0x01,
  auth: 0x02,
  auth_psk: 0x03
}
VERSION =
"1.0.2"

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Util

#i2osp, #os2ip, #xor

Constructor Details

#initialize(kem_id, kdf_id, aead_id) ⇒ HPKE

Returns a new instance of HPKE.

Raises:

  • (Exception)


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/hpke.rb', line 74

def initialize(kem_id, kdf_id, aead_id)
  raise Exception.new('Unsupported AEAD') unless AVAILABLE_AEAD.include?(aead_id)

  @kem = case kem_id
          when DHKEM_P256_HKDF_SHA256 then DHKEM::EC::P_256.new(HKDF_SHA256)
          when DHKEM_P384_HKDF_SHA384 then DHKEM::EC::P_384.new(HKDF_SHA384)
          when DHKEM_P521_HKDF_SHA512 then DHKEM::EC::P_521.new(HKDF_SHA512)
          when DHKEM_X25519_HKDF_SHA256 then DHKEM::X25519.new(HKDF_SHA256)
          when DHKEM_X448_HKDF_SHA512 then DHKEM::X448.new(HKDF_SHA512)
          else raise Exception.new('Unsupported KEM')
          end
  @hkdf = case kdf_id
           when HKDF_SHA256 then HKDF.new(HKDF_SHA256)
           when HKDF_SHA384 then HKDF.new(HKDF_SHA384)
           when HKDF_SHA512 then HKDF.new(HKDF_SHA512)
           else raise Exception.new('Unsupported KDF')
           end
  @aead_id = aead_id
  @aead_name = CIPHERS[aead_id][:name]
  @n_k = CIPHERS[aead_id][:n_k]
  @n_n = CIPHERS[aead_id][:n_n]
  @n_t = CIPHERS[aead_id][:n_t]
end

Instance Attribute Details

#aead_idObject (readonly)

Returns the value of attribute aead_id.



12
13
14
# File 'lib/hpke.rb', line 12

def aead_id
  @aead_id
end

#aead_nameObject (readonly)

Returns the value of attribute aead_name.



12
13
14
# File 'lib/hpke.rb', line 12

def aead_name
  @aead_name
end

#hkdfObject (readonly)

Returns the value of attribute hkdf.



12
13
14
# File 'lib/hpke.rb', line 12

def hkdf
  @hkdf
end

#kemObject (readonly)

Returns the value of attribute kem.



12
13
14
# File 'lib/hpke.rb', line 12

def kem
  @kem
end

#n_kObject (readonly)

Returns the value of attribute n_k.



12
13
14
# File 'lib/hpke.rb', line 12

def n_k
  @n_k
end

#n_nObject (readonly)

Returns the value of attribute n_n.



12
13
14
# File 'lib/hpke.rb', line 12

def n_n
  @n_n
end

#n_tObject (readonly)

Returns the value of attribute n_t.



12
13
14
# File 'lib/hpke.rb', line 12

def n_t
  @n_t
end

Instance Method Details

#aead_decrypt(key, nonce, aad, ct) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/hpke.rb', line 207

def aead_decrypt(key, nonce, aad, ct)
  ct_body = ct[0, ct.length - n_t]
  tag = ct[-n_t, n_t]
  cipher = OpenSSL::Cipher.new(aead_name)
  cipher.decrypt
  cipher.key = key
  cipher.iv = nonce
  cipher.auth_tag = tag
  cipher.auth_data = aad
  cipher.padding = 0
  cipher.update(ct_body) << cipher.final
end

#aead_encrypt(key, nonce, aad, pt) ⇒ Object



196
197
198
199
200
201
202
203
204
205
# File 'lib/hpke.rb', line 196

def aead_encrypt(key, nonce, aad, pt)
  cipher = OpenSSL::Cipher.new(aead_name)
  cipher.encrypt
  cipher.key = key
  cipher.iv = nonce
  cipher.auth_data = aad
  cipher.padding = 0
  s = cipher.update(pt) << cipher.final
  s + cipher.auth_tag
end

#export(exporter_secret, exporter_context, len) ⇒ Object



192
193
194
# File 'lib/hpke.rb', line 192

def export(exporter_secret, exporter_context, len)
  @hkdf.labeled_expand(exporter_secret, 'sec', exporter_context, len, suite_id)
end

#setup_auth_psk_r(enc, sk_r, info, psk, psk_id, pk_s) ⇒ Object



150
151
152
153
# File 'lib/hpke.rb', line 150

def setup_auth_psk_r(enc, sk_r, info, psk, psk_id, pk_s)
  shared_secret = @kem.auth_decap(enc, sk_r, pk_s)
  key_schedule_r(MODES[:auth_psk], shared_secret, info, psk, psk_id)
end

#setup_auth_psk_s(pk_r, info, psk, psk_id, sk_s) ⇒ Object



141
142
143
144
145
146
147
148
# File 'lib/hpke.rb', line 141

def setup_auth_psk_s(pk_r, info, psk, psk_id, sk_s)
  encap_result = @kem.auth_encap(pk_r, sk_s)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:auth_psk], encap_result[:shared_secret], info, psk, psk_id),
    shared_secret: encap_result[:shared_secret]
  }
end

#setup_auth_psk_s_fixed(pk_r, info, psk, psk_id, sk_s, ikm_e) ⇒ Object



183
184
185
186
187
188
189
190
# File 'lib/hpke.rb', line 183

def setup_auth_psk_s_fixed(pk_r, info, psk, psk_id, sk_s, ikm_e)
  encap_result = @kem.auth_encap_fixed(pk_r, sk_s, ikm_e)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:auth_psk], encap_result[:shared_secret], info, psk, psk_id),
    shared_secret: encap_result[:shared_secret]
  }
end

#setup_auth_r(enc, sk_r, info, pk_s) ⇒ Object



136
137
138
139
# File 'lib/hpke.rb', line 136

def setup_auth_r(enc, sk_r, info, pk_s)
  shared_secret = @kem.auth_decap(enc, sk_r, pk_s)
  key_schedule_r(MODES[:auth], shared_secret, info, DEFAULT_PSK, DEFAULT_PSK_ID)
end

#setup_auth_s(pk_r, info, sk_s) ⇒ Object



127
128
129
130
131
132
133
134
# File 'lib/hpke.rb', line 127

def setup_auth_s(pk_r, info, sk_s)
  encap_result = @kem.auth_encap(pk_r, sk_s)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:auth], encap_result[:shared_secret], info, DEFAULT_PSK, DEFAULT_PSK_ID),
    shared_secret: encap_result[:shared_secret]
  }
end

#setup_auth_s_fixed(pk_r, info, sk_s, ikm_e) ⇒ Object



174
175
176
177
178
179
180
181
# File 'lib/hpke.rb', line 174

def setup_auth_s_fixed(pk_r, info, sk_s, ikm_e)
  encap_result = @kem.auth_encap_fixed(pk_r, sk_s, ikm_e)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:auth], encap_result[:shared_secret], info, DEFAULT_PSK, DEFAULT_PSK_ID),
    shared_secret: encap_result[:shared_secret]
  }
end

#setup_base_r(enc, sk_r, info) ⇒ Object



108
109
110
111
# File 'lib/hpke.rb', line 108

def setup_base_r(enc, sk_r, info)
  shared_secret = @kem.decap(enc, sk_r)
  key_schedule_r(MODES[:base], shared_secret, info, DEFAULT_PSK, DEFAULT_PSK_ID)
end

#setup_base_s(pk_r, info) ⇒ Object

public facing APIs



99
100
101
102
103
104
105
106
# File 'lib/hpke.rb', line 99

def setup_base_s(pk_r, info)
  encap_result = @kem.encap(pk_r)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:base], encap_result[:shared_secret], info, DEFAULT_PSK, DEFAULT_PSK_ID),
    shared_secret: encap_result[:shared_secret]
  }
end

#setup_base_s_fixed(pk_r, info, ikm_e) ⇒ Object

for testing purposes



156
157
158
159
160
161
162
163
# File 'lib/hpke.rb', line 156

def setup_base_s_fixed(pk_r, info, ikm_e)
  encap_result = @kem.encap_fixed(pk_r, ikm_e)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:base], encap_result[:shared_secret], info, DEFAULT_PSK, DEFAULT_PSK_ID),
    shared_secret: encap_result[:shared_secret]
  }
end

#setup_psk_r(enc, sk_r, info, psk, psk_id) ⇒ Object



122
123
124
125
# File 'lib/hpke.rb', line 122

def setup_psk_r(enc, sk_r, info, psk, psk_id)
  shared_secret = @kem.decap(enc, sk_r)
  key_schedule_r(MODES[:psk], shared_secret, info, psk, psk_id)
end

#setup_psk_s(pk_r, info, psk, psk_id) ⇒ Object



113
114
115
116
117
118
119
120
# File 'lib/hpke.rb', line 113

def setup_psk_s(pk_r, info, psk, psk_id)
  encap_result = @kem.encap(pk_r)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:psk], encap_result[:shared_secret], info, psk, psk_id),
    shared_secret: encap_result[:shared_secret]
  }
end

#setup_psk_s_fixed(pk_r, info, psk, psk_id, ikm_e) ⇒ Object



165
166
167
168
169
170
171
172
# File 'lib/hpke.rb', line 165

def setup_psk_s_fixed(pk_r, info, psk, psk_id, ikm_e)
  encap_result = @kem.encap_fixed(pk_r, ikm_e)
  {
    enc: encap_result[:enc],
    context_s: key_schedule_s(MODES[:psk], encap_result[:shared_secret], info, psk, psk_id),
    shared_secret: encap_result[:shared_secret]
  }
end