Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new generic function for destructuring EC points #61

Closed
wants to merge 2 commits into from

Conversation

dnaeon
Copy link
Contributor

@dnaeon dnaeon commented Jul 4, 2023

This PR adds a new generic function - EC-DESTRUCTURE-POINT and provides implementation for the existing EC point classes.

Also, it exports the various EC point operations and adds slot readers.

@glv2
Copy link
Collaborator

glv2 commented Jul 4, 2023

The EC-DESTRUCTURE-POINT function as you wrote it will return the internal representation of points, which is not the regular (x,y) coordinates of a point on the curve.
Internally we're using projective coordinates which make calculations much faster by avoiding point divisions. A single (x, y) point can have many different but equivalent (X, Y, Z) projective coordinates.

Do you really need to know the internal representation of points, or is it the "official" curve coordinates that you are interested in?

@dnaeon
Copy link
Contributor Author

dnaeon commented Jul 4, 2023

Hey @glv2 ,

Thanks for the quick reply!

I have this example JWK public key.

{
    "kty": "EC",
    "use": "sig",
    "crv": "P-256",
    "kid": "test-id",
    "x": "Nyp0YT9NRlY44cULzFiGnWzF_BsaOwyxmsukKkbvuKA",
    "y": "joRM15S51FU8NNwMpN03mndkxNcK_5cTOn8bVEBy9KU",
    "alg": "ES256"
}

In order to decode above key I'm using the following code.

(defun decode-secp256r1-pk (data)
  "Decodes Secp256r1 (NIST P-256) public key from the given plist data.
See RFC 7518, Section 6.2.1 for more details about Elliptic Curve
public keys format."
  (let* ((use (getf data :|use|))
         (alg (getf data :|alg|))
         (kid (getf data :|kid|))
         (kty (getf data :|kty|))
         (key-ops (getf data :|key_ops|))
         (crv (getf data :|crv|))
         (x (getf data :|x|))
         (y (getf data :|y|)))
    (unless x
      (error 'invalid-key :message "Missing X coordinate parameter" :data data))
    (unless y
      (error 'invalid-key :message "Missing Y coordinate parameter" :data data))
    ;; The X and Y coordinates are Base64urlUInt-encoded values
    (let* ((x-octets (binascii:decode-base64url x))
           (y-octets (binascii:decode-base64url y))
           (x-uint (ironclad:ec-decode-scalar :secp256r1 x-octets))
           (y-uint (ironclad:ec-decode-scalar :secp256r1 y-octets))
           (point (make-instance 'ironclad:secp256r1-point :x x-uint :y y-uint :z 1)))
      (unless (and (= (length x-octets) 32)
                   (= (length y-octets) 32))
        (error 'invalid-key :message "Coordinates should be 32 bytes for Secp256r1 key" :data data))
      (list :use use
            :alg alg
            :kid kid
            :kty kty
            :key-ops key-ops
            :crv crv
            :key (ironclad:make-public-key :secp256r1 :y (ironclad:ec-encode-point point))))))

Then on the REPL:

CL-USER> (defparameter *key-plist*
	   (jonathan:parse (uiop:read-file-string #P"/path/to/ec-public-key.json") :as :plist))
*KEY-PLIST*

CL-USER> (defparameter *parsed-key* (decode-secp256r1-pk *key-plist*))
*PARSED-KEY*

Now, in order to encode back the *PARSED-KEY* into JWK format, I need to add x and y coordinates in the resulting JSON.

The only way I could do that so far is if get y using (ironclad:secp256r1-key-y (getf *parsed-key* :key), then use (ironclad:ec-decode-point :secp256r1 y) in order to get the point.

And finally I could use (ironclad:ec-destructure-point point) in order to get x and y coords, which will then be encoded in the resulting JWK JSON.

Is there another way I should be approaching this, or is this the way to go?

Thanks!

@glv2
Copy link
Collaborator

glv2 commented Jul 4, 2023

I added ec-make-point and ec-destructure-point functions (5a467eb) containing the appropriate coordinate conversions. I also added a modified version of the second patch of this pull request, to export EC-related functions (2da4f5e).

So now you should be able to do:

(let* ((coordinates (ec-destructure-point p))
       (x-uint (getf coordinates :x))
       (y-uint (getf coordinates :y))
  ...)

(ec-make-point :secp256r1 :x x-uint :y y-uint)

Could you give it a try to see if something is missing?

@dnaeon
Copy link
Contributor Author

dnaeon commented Jul 4, 2023

Hey @glv2 ,

I'll try it out soon and get back to you.

Thanks!

@dnaeon
Copy link
Contributor Author

dnaeon commented Jul 4, 2023

@glv2 , everything seems to work fine, thanks!

@dnaeon dnaeon closed this Jul 4, 2023
dnaeon added a commit to dnaeon/cl-jwk that referenced this pull request Jul 4, 2023
dnaeon added a commit to dnaeon/cl-jwk that referenced this pull request Jul 4, 2023
Until [1] is merged we will use the unexported symbols

[1]: sharplispers/ironclad#61
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants