taoensso / tempel

Data security framework for Clojure
https://www.taoensso.com/tempel
Eclipse Public License 1.0
125 stars 2 forks source link

API for building PasswordEncoder? #13

Closed ieugen closed 3 months ago

ieugen commented 3 months ago

Hello,

I'm implementing a pac4j integration for Ring . One of the things that I was looking at is to provide a password encoder and I thought I could to this with tempel. However there is no API to do this with tempel. You have an API for encrypting data, but not one for hashing the password - or at least I did not figure it out at first glance.

Does it make sense to have an API that allows users to plug in tempel with other tools to provide the same password encryption?

This is how the password encoder is used in pac4j https://www.pac4j.org/docs/authenticators.html#2-passwordencoder .

Thanks, Eugen

ptaoussanis commented 3 months ago

@ieugen Hi Eugen!

I'm not too sure what you mean by "provide a password encoder" or "plug in tempel with other tools to provide the same password encryption".

Could you maybe provide a simple example of what problem you're trying to solve?

You'd like to use Tempel to encrypt a payload, but use this pac4j library for password-based key derivation?

ieugen commented 3 months ago

Hi @ptaoussanis ,

Well, a naive implementation of the password encoder interface looks like this:

You would use bcrypt/scrypt/pbkdf2 to encode the passwords and save them in DB. pac4j comes with some implementations (wrappers) for Spring Security Crypto, Apache Shiro PasswordService and for jBCrypt library. https://www.pac4j.org/docs/authenticators.html#2-passwordencoder .

I would like to have a tempel password encoder that will allow me to encode passwords (for db/file persistence) in a format interoperable with tempel.

(defn make-pac4j-tempel-password-encoder
  "Implement pac4j ^org.pac4j.core.credentials.password.PasswordEncoder 
   using clojure tempel library.

   Return an instance of a ^org.pac4j.core.credentials.password.PasswordEncoder."
  ([]
   (reify PasswordEncoder
     (encode [_this password-str]
       (log/info "Encode password" password-str)
       (str/reverse password-str))
     (matches [this plain-pw-str encoded-pw]
       (log/info "Matches: " plain-pw-str "-" encoded-pw)
       (let [ep (.encode this plain-pw-str)]
         (= ep encoded-pw))))))

^:rct/test
(comment 

  (def encoder (make-pac4j-tempel-password-encoder))

  (.encode encoder "user-password")
  ;; => "drowssap-resu"

  (.matches encoder "user-password" "drowssap-resu")
  ;; => true 

  )
ptaoussanis commented 3 months ago

@ieugen Thanks for the added details.

If you'd bear with me a moment though- I'd find it helpful if we could step back from the implementation/interface level, and first clarify in very simple language - what problem you're actually trying to solve. Please imagine that you're trying to explain your use-case to a 5-year-old :-)

It currently sounds like:

You want to encrypt and decrypt data using Tempel. And to do this you want to use a key created by pac4j, derived from a password.

But if I've got that wrong, it'd be helpful to have the above points spelled out. Thanks! 🙏

ieugen commented 3 months ago

Hi, Thanks for looking into this.

I want to use tempel in a system (will need to encrypt some certificates in DB / FS). In that system I also do username + password authentication (alongside other auth options). I would like to use the the same encryption tempel provides in my authentication system. The authentication system in my case is pac4j so I have to integrate with that API. I don't want to use the password encoding implementations provided by pac4j.

The pac4j API provides an interface to encode plain text user passwords for storage so that we can later authenticate username+password.

Tempel does implement password encoding for storage but does not directly expose that API in a way that is suitable for integrating with pac4j / external tooling.

ieugen commented 3 months ago

Looking at the source code, I guess I am looking for https://github.com/taoensso/tempel/blob/master/src/taoensso/tempel/pbkdf.clj . But it's marked private and not documented (no docs published).

ptaoussanis commented 3 months ago

Thanks for looking into this.

You're welcome 👍

I would like to use the the same encryption tempel provides in my authentication system.

I'm sorry, but I'm not sure what exactly this means. It really would help if you could please directly address the questions I'm asking above. I know this may seem unnecessarily restrictive, but it'd really help me understand what you're trying to do 🙏

I'll attempt to fill in the answers and you can tell me if I'm wrong-

You want to encrypt and decrypt some data - is that correct?

It sounds like the answer is yes - is that right?

Do you want Tempel to do the encryption and decryption, or some other library?

It sounds like the answer is yes - is that right?

You want to encrypt and decrypt using a password, or key somehow derived from a password - is that correct?

This part maybe seems a bit murky. You're using pac4j for authentication and pac4j supports both password-based authentication, and maybe some other methods.

And you're unclear on how to use Tempel to encrypt and decrypt data with a key somehow provided by pac4j?

For example: user Alice has authenticated herself using pac4j, and you'd now like her to be able to encrypt and decrypt data using Tempel?

(Please note that encryption and decryption fundamentally implies the existence of a key. So if we're talking about encryption and decryption, we need to identify where the key is coming from.)

Do you want Tempel to do the key derivation (i.e. transform a password string to an encryption key), or some other library?

As above, it sounds like maybe this part is a bit murky.

I don't want to use the password encoding implementations provided by pac4j.

So I'm lost here. Let's restrict ourselves first to the simple case of a user authenticating with a password:

  1. User Alice creates a password, "Alice's password".
  2. She provides her password to authenticate via pac4j.
  3. We now want to use Tempel to encrypt data for Alice.

Is that right so far?

If so, step 3 requires a key. Do you have in mind where you want that key to come from? The obvious place I'd expect it to come from is the password she provided at step 1.

Is that what you want in this particular simple example, or no?

If that is what you'd want, that would mean using a key provided by pac4j. If that isn't what you want, then would you please clarify what key you want to use in this case?

Again, let's please step away from code for a moment and just talk conceptually.

ieugen commented 3 months ago

Hi Peter,

I'm trying to implement password hashing using tempel API.

In your example above,

User Alice creates a password, "Alice's password". She provides her password to authenticate via pac4j. We now want to use Tempel to encrypt data for Alice.

Alice sends an HTTP request with username: "alice" and password "s3cret" . System checks if Alice is allowed to access the system. I want to implement the check. For that I would like to load the hash for user "alice" from DB, hash "s3cret" using the same parameters and compare the two.
Nothing more. It's a common use case when integrating. See https://github.com/apache/shiro/blob/main/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java and https://docs.spring.io/spring-security/reference/features/authentication/password-storage.html

ieugen commented 3 months ago

I'll probably use the spring implementation and not try to implement one with tempel. But would be nice for tempel to allow interop with other frameworks / libraries.

ptaoussanis commented 3 months ago

I'm sorry, but I'm bowing out since I'm finding this interaction needlessly frustrating.

You keep linking me to interface definitions and long-form documentation instead of answering a few very straight-forward questions that I keep politely asking you to directly address.

ieugen commented 3 months ago

Sorry for that. I guess I don't understand what you don't understand.

Not trying to encrypt anything with tempel during Authentication. I think Spring documentation explains it best - it's about password storage - which IMO is very much in the area of data security.

Spring Security’s PasswordEncoder interface is used to perform a one-way transformation of a password to let the password be stored securely. Given PasswordEncoder is a one-way transformation, it is not useful when the password transformation needs to be two-way (such as storing credentials used to authenticate to a database). Typically, PasswordEncoder is used for storing a password that needs to be compared to a user-provided password at the time of authentication.