Skip to content

Commit

Permalink
Merge pull request #76 from jhiemstrawisc/fix-namespace-jwks
Browse files Browse the repository at this point in the history
Use lestrrat jwks instead of custom structs in NS Registry
  • Loading branch information
bbockelm authored Sep 2, 2023
2 parents 6fc6901 + 50156a7 commit 4831990
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 88 deletions.
28 changes: 0 additions & 28 deletions config/init_server_creds.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"fmt"
"math/big"
Expand Down Expand Up @@ -251,30 +250,3 @@ func GetOriginJWK() (*jwk.Key, error) {
}
return key, nil
}

func JWKSMap(jwks *jwk.Set) (map[string]string, error) {
// Marshal the set into JSON
jsonBytes, err := json.MarshalIndent(jwks, "", " ")
if err != nil {
return nil, err
}

// Parse the JSON into a structure we can manipulate
var parsed map[string][]map[string]interface{}
err = json.Unmarshal(jsonBytes, &parsed)
if err != nil {
return nil, err
}

// Convert the map[string]interface{} to map[string]string
stringMaps := make([]map[string]string, len(parsed["keys"]))
for i, m := range parsed["keys"] {
stringMap := make(map[string]string)
for k, v := range m {
stringMap[k] = fmt.Sprintf("%v", v)
}
stringMaps[i] = stringMap
}

return stringMaps[0], nil
}
48 changes: 25 additions & 23 deletions namespace-registry/client_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ package nsregistry

import (
"crypto/tls"

"github.com/pkg/errors"

"bufio"
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"os"

"github.com/pelicanplatform/pelican/config"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)

Expand Down Expand Up @@ -136,9 +136,24 @@ func NamespaceRegister(privateKeyPath string, namespaceRegistryEndpoint string,
return errors.Wrap(err, "Failed to retrieve public key")
}

jwks, err := config.JWKSMap(publicKey)
if err != nil {
return errors.Wrap(err, "Failed to convert public key to JWKS")
/*
* TODO: For now, we only allow namespace registration to occur with a single key, but
* at some point we should expose an API for adding additional pubkeys to each
* namespace. There is a similar TODO listed in registry.go, as the choices made
* there mirror the choices made here.
* To enforce that we're only trying to register one key, we check the length here
*/
if (*publicKey).Len() > 1 {
return errors.Errorf("Only one public key can be registered in this step, but %d were provided\n", (*publicKey).Len())
}

if log.IsLevelEnabled(log.DebugLevel) {
// Let's check that we can convert to JSON and get the right thing...
jsonbuf, err := json.Marshal(publicKey)
if err != nil {
return errors.Wrap(err, "failed to marshal the public key into JWKS JSON")
}
log.Debugln("Constructed JWKS from loading public key:", string(jsonbuf))
}

privateKey, err := config.LoadPrivateKey(privateKeyPath)
Expand All @@ -153,7 +168,7 @@ func NamespaceRegister(privateKeyPath string, namespaceRegistryEndpoint string,

data := map[string]interface{}{
"client_nonce": clientNonce,
"pubkey": fmt.Sprintf("%x", jwks["x"]),
"pubkey": publicKey,
}

resp, err := makeRequest(namespaceRegistryEndpoint, "POST", data)
Expand Down Expand Up @@ -181,24 +196,11 @@ func NamespaceRegister(privateKeyPath string, namespaceRegistryEndpoint string,
return errors.Wrap(err, "Failed to sign payload")
}

// Create data for the second POST request
xBytes, err := base64.RawURLEncoding.DecodeString(jwks["x"])
if err != nil {
return errors.Wrap(err, "Failed to decode jwks.x")
}
yBytes, err := base64.RawURLEncoding.DecodeString(jwks["y"])
if err != nil {
return errors.Wrap(err, "Failed to decode jwks.y")
}

// // Create data for the second POST request
unidentifiedPayload := map[string]interface{}{
"client_nonce": clientNonce,
"server_nonce": respData.ServerNonce,
"pubkey": map[string]string{
"x": new(big.Int).SetBytes(xBytes).String(),
"y": new(big.Int).SetBytes(yBytes).String(),
"curve": jwks["crv"],
},
"client_nonce": clientNonce,
"server_nonce": respData.ServerNonce,
"pubkey": publicKey,
"client_payload": clientPayload,
"client_signature": hex.EncodeToString(signature),
"server_payload": respData.ServerPayload,
Expand Down
72 changes: 35 additions & 37 deletions namespace-registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,27 @@
package nsregistry

import (
// "context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"github.com/gin-gonic/gin"
"github.com/pelicanplatform/pelican/config"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"io"
"math/big"
"net/http"
"net/url"
"os"
"strings"
"sync"

"github.com/gin-gonic/gin"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/pelicanplatform/pelican/config"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"

// use this sqlite driver instead of the one from
// github.com/mattn/go-sqlite3, because this one
// doesn't require compilation with CGO_ENABLED
Expand Down Expand Up @@ -249,49 +250,46 @@ func keySignChallengeInit(ctx *gin.Context, data *registrationData) error {
return nil
}

type jwks struct {
X string `json:"x"`
Y string `json:"y"`
}

func jwksToEcdsaPublicKey(jwks *jwks) (*ecdsa.PublicKey, error) {
xBigInt, err := new(big.Int).SetString(jwks.X, 10)
if !err {
return nil, errors.New("Failed to convert jwks.x to Big Int")
}
yBigInt, err := new(big.Int).SetString(jwks.Y, 10)
if !err {
return nil, errors.New("Failed to convert jwks.y to Big Int")
func keySignChallengeCommit(ctx *gin.Context, data *registrationData, action string) error {
// Parse the client's jwks as a set here
clientJwks, err := jwk.Parse(data.Pubkey)
if err != nil {
return errors.Wrap(err, "Couldn't parse the pubkey from the client")
}

clientPubkey := &ecdsa.PublicKey{
Curve: elliptic.P521(),
X: xBigInt,
Y: yBigInt,
if log.IsLevelEnabled(log.DebugLevel) {
// Let's check that we can convert to JSON and get the right thing...
jsonbuf, err := json.Marshal(clientJwks)
if err != nil {
return errors.Wrap(err, "failed to marshal the client's keyset into JSON")
}
log.Debugln("Client JWKS as seen by the registry server:", string(jsonbuf))
}

return clientPubkey, nil
}

func keySignChallengeCommit(ctx *gin.Context, data *registrationData, action string) error {
var pubkeyJwks jwks
if err := json.Unmarshal(data.Pubkey, &pubkeyJwks); err != nil {
ctx.JSON(500, gin.H{"error": "Failed to unmarshal the provided pubkey"})
return errors.Wrap(err, "Failed to unmarshal the provided pubkey")
/*
* TODO: This section makes the assumption that the incoming jwks only contains a single
* key, a property that is enforced by the client at the origin. Eventually we need
* to support the addition of other keys in the jwks stored for the origin. There is
* a similar TODO listed in client_commands.go, as the choices made there mirror the
* choices made here.
*/
key, exists := clientJwks.Key(0)
if !exists {
return errors.New("There was no key at index 0 in the client's JWKS. Something is wrong")
}

clientPubkey, err := jwksToEcdsaPublicKey(&pubkeyJwks)
if err != nil {
return errors.Wrap(err, "Failed to convert jwks to ECDSA pubkey")
var rawkey interface{} // This is the raw key, like *rsa.PrivateKey or *ecdsa.PrivateKey
if err := key.Raw(&rawkey); err != nil {
return errors.Wrap(err, "failed to generate raw pubkey from jwks")
}

clientPayload := []byte(data.ClientNonce + data.ServerNonce)
clientSignature, err := hex.DecodeString(data.ClientSignature)
if err != nil {
ctx.JSON(500, gin.H{"error": "Failed to decode client's signature"})
return errors.Wrap(err, "Failed to decode the client's signature")
}
clientVerified := verifySignature(clientPayload, clientSignature, clientPubkey)

clientVerified := verifySignature(clientPayload, clientSignature, (rawkey).(*ecdsa.PublicKey))
serverPayload, err := hex.DecodeString(data.ServerPayload)
if err != nil {
ctx.JSON(500, gin.H{"error": "Failed to decode the server's payload"})
Expand Down

0 comments on commit 4831990

Please sign in to comment.