|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +module Net |
| 4 | + class IMAP |
| 5 | + module SASL |
| 6 | + |
| 7 | + # For method descriptions, see {RFC5802 |
| 8 | + # §2}[https://www.rfc-editor.org/rfc/rfc5802#section-2] and {RFC5802 |
| 9 | + # §3}[https://www.rfc-editor.org/rfc/rfc5802#section-3]. |
| 10 | + # |
| 11 | + # Expects: |
| 12 | + # * #Hi, #H, and #HMAC use: |
| 13 | + # * +#digest+ --- an OpenSSL::Digest. |
| 14 | + # * #salted_password uses: |
| 15 | + # * +#salt+ and +#iterations+ --- the server's values for this user |
| 16 | + # * +#password+ |
| 17 | + # * #auth_message is built from: |
| 18 | + # * +#client_first_message_bare+ --- contains +#cnonce+ |
| 19 | + # * +#server_first_message+ --- contains +#snonce+ |
| 20 | + # * +#client_final_message_no_proof+ --- contains +#snonce+ |
| 21 | + module ScramAlgorithm |
| 22 | + def Normalize(str) SASL.saslprep(str) end |
| 23 | + |
| 24 | + def Hi(str, salt, iterations) |
| 25 | + length = digest.digest_length |
| 26 | + OpenSSL::KDF.pbkdf2_hmac( |
| 27 | + str, |
| 28 | + salt: salt, |
| 29 | + iterations: iterations, |
| 30 | + length: length, |
| 31 | + hash: digest, |
| 32 | + ) |
| 33 | + end |
| 34 | + |
| 35 | + def H(str) digest.digest str end |
| 36 | + |
| 37 | + def HMAC(key, data) OpenSSL::HMAC.digest(digest, key, data) end |
| 38 | + |
| 39 | + def XOR(str1, str2) |
| 40 | + str1.unpack("C*") |
| 41 | + .zip(str2.unpack("C*")) |
| 42 | + .map {|a, b| a ^ b } |
| 43 | + .pack("C*") |
| 44 | + end |
| 45 | + |
| 46 | + def auth_message |
| 47 | + [ |
| 48 | + client_first_message_bare, |
| 49 | + server_first_message, |
| 50 | + client_final_message_no_proof, |
| 51 | + ] |
| 52 | + .join(",") |
| 53 | + end |
| 54 | + |
| 55 | + def salted_password |
| 56 | + Hi(Normalize(password), salt, iterations) |
| 57 | + end |
| 58 | + |
| 59 | + def client_key; HMAC(salted_password, "Client Key") end |
| 60 | + def server_key; HMAC(salted_password, "Server Key") end |
| 61 | + def stored_key; H(client_key) end |
| 62 | + def client_signature; HMAC(stored_key, auth_message) end |
| 63 | + def server_signature; HMAC(server_key, auth_message) end |
| 64 | + def client_proof; XOR(client_key, client_signature) end |
| 65 | + end |
| 66 | + |
| 67 | + end |
| 68 | + end |
| 69 | +end |
0 commit comments