Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ Note: support was added to provision a set of fixture data into the database.
Run `go run . provision fixtures -h` for more information.

### Test

```bash
grpcurl -plaintext localhost:8080 list

Expand Down
1 change: 1 addition & 0 deletions sdk/auth/access_token_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ type AccessTokenSource interface {
DecryptWithDPoPKey(data []byte) ([]byte, error)
MakeToken(func(jwk.Key) ([]byte, error)) ([]byte, error)
DPoPPublicKeyPEM() string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can get rid of DPoPPublicKeyPEM, I think

EncryptionPublicKeyPEM() string
RefreshAccessToken() error
}
1 change: 1 addition & 0 deletions sdk/auth/authn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func (fake FakeAccessTokenSource) MakeToken(tokenMaker func(jwk.Key) ([]byte, er
func (fake FakeAccessTokenSource) DPoPPublicKeyPEM() string {
return "this is the PEM"
}
func (fake FakeAccessTokenSource) EncryptionPublicKeyPEM() string { return "this is the PEM" }
func (fake FakeAccessTokenSource) RefreshAccessToken() error {
return errors.New("can't refresh this one")
}
Expand Down
98 changes: 55 additions & 43 deletions sdk/idp_access_token_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,94 +22,102 @@ const (
dpopKeySize = 2048
)

func getNewDPoPKey() (string, jwk.Key, *crypto.AsymDecryption, error) { //nolint:ireturn // this is only internal
dpopPrivate, err := rsa.GenerateKey(rand.Reader, dpopKeySize)
func generateKeyPair() (string, string, jwk.Key, error) {
rawPrivateKey, err := rsa.GenerateKey(rand.Reader, dpopKeySize)
if err != nil {
return "", nil, nil, fmt.Errorf("error creating DPoP keypair: %w", err)
return "", "", nil, fmt.Errorf("error creating DPoP keypair: %w", err)
}
dpopKey, err := jwk.FromRaw(dpopPrivate)
jwkPrivateKey, err := jwk.FromRaw(rawPrivateKey)
if err != nil {
return "", nil, nil, fmt.Errorf("error creating JWK: %w", err)
return "", "", nil, fmt.Errorf("error creating JWK: %w", err)
}
err = dpopKey.Set("alg", jwa.RS256)
err = jwkPrivateKey.Set("alg", jwa.RS256)
if err != nil {
return "", nil, nil, fmt.Errorf("error setting the key algorithm: %w", err)
return "", "", nil, fmt.Errorf("error setting the key algorithm: %w", err)
}

dpopKeyDER, err := x509.MarshalPKCS8PrivateKey(dpopPrivate)
derPrivateKey, err := x509.MarshalPKCS8PrivateKey(rawPrivateKey)
if err != nil {
return "", nil, nil, fmt.Errorf("error marshalling private key: %w", err)
return "", "", nil, fmt.Errorf("error marshalling private key: %w", err)
}

var dpopPrivatePEM strings.Builder
var privateKeyPem strings.Builder

err = pem.Encode(&dpopPrivatePEM, &pem.Block{
err = pem.Encode(&privateKeyPem, &pem.Block{
Type: "PRIVATE KEY",
Bytes: dpopKeyDER,
Bytes: derPrivateKey,
})
if err != nil {
return "", nil, nil, fmt.Errorf("error encoding private key to PEM")
return "", "", nil, fmt.Errorf("error encoding private key to PEM")
}

dpopPublic := dpopPrivate.Public()
dpopPublicDER, err := x509.MarshalPKIXPublicKey(dpopPublic)
rawPublicKey := rawPrivateKey.Public()
derPublicKey, err := x509.MarshalPKIXPublicKey(rawPublicKey)
if err != nil {
return "", nil, nil, fmt.Errorf("error marshalling public key: %w", err)
return "", "", nil, fmt.Errorf("error marshalling public key: %w", err)
}

var dpopPublicKeyPEM strings.Builder
err = pem.Encode(&dpopPublicKeyPEM, &pem.Block{
var publicKeyPem strings.Builder
err = pem.Encode(&publicKeyPem, &pem.Block{
Type: "PUBLIC KEY",
Bytes: dpopPublicDER,
Bytes: derPublicKey,
})
if err != nil {
return "", nil, nil, fmt.Errorf("error encoding public key to PEM")
return "", "", nil, fmt.Errorf("error encoding public key to PEM")
}

asymDecryption, err := crypto.NewAsymDecryption(dpopPrivatePEM.String())
if err != nil {
return "", nil, nil, fmt.Errorf("error creating asymmetric decryptor: %w", err)
}

return dpopPublicKeyPEM.String(), dpopKey, &asymDecryption, nil
return publicKeyPem.String(), privateKeyPem.String(), jwkPrivateKey, nil
}

/*
Credentials that allow us to connect to an IDP and obtain an access token that is bound
to a DPoP key
*/
type IDPAccessTokenSource struct {
credentials oauth.ClientCredentials
idpTokenEndpoint url.URL
token *oauth2.Token
scopes []string
dpopKey jwk.Key
asymDecryption crypto.AsymDecryption
dpopPEM string
tokenMutex *sync.Mutex
credentials oauth.ClientCredentials
idpTokenEndpoint url.URL
token *oauth2.Token
scopes []string
dpopKey jwk.Key
encryptionPublicKeyPEM string
asymDecryption crypto.AsymDecryption
dpopPEM string
tokenMutex *sync.Mutex
}

func NewIDPAccessTokenSource(
credentials oauth.ClientCredentials, idpTokenEndpoint string, scopes []string) (IDPAccessTokenSource, error) {
endpoint, err := url.Parse(idpTokenEndpoint)

if err != nil {
return IDPAccessTokenSource{}, fmt.Errorf("invalid url [%s]: %w", idpTokenEndpoint, err)
}

dpopPublicKeyPEM, dpopKey, asymDecryption, err := getNewDPoPKey()
dpopPublicKeyPem, _, dpopKey, err := generateKeyPair()
if err != nil {
return IDPAccessTokenSource{}, err
}

encryptionPublicKeyPem, encryptionPrivateKeyPem, _, err := generateKeyPair()
if err != nil {
return IDPAccessTokenSource{}, err
}

asymDecryption, err := crypto.NewAsymDecryption(encryptionPrivateKeyPem)
if err != nil {
return IDPAccessTokenSource{}, err
}

creds := IDPAccessTokenSource{
credentials: credentials,
idpTokenEndpoint: *endpoint,
token: nil,
scopes: scopes,
asymDecryption: *asymDecryption,
dpopKey: dpopKey,
dpopPEM: dpopPublicKeyPEM,
tokenMutex: &sync.Mutex{},
credentials: credentials,
idpTokenEndpoint: *endpoint,
token: nil,
scopes: scopes,
asymDecryption: asymDecryption,
encryptionPublicKeyPEM: encryptionPublicKeyPem,
dpopKey: dpopKey,
dpopPEM: dpopPublicKeyPem,
tokenMutex: &sync.Mutex{},
}

return creds, nil
Expand Down Expand Up @@ -151,3 +159,7 @@ func (t *IDPAccessTokenSource) MakeToken(tokenMaker func(jwk.Key) ([]byte, error
func (t *IDPAccessTokenSource) DPoPPublicKeyPEM() string {
return t.dpopPEM
}

func (t *IDPAccessTokenSource) EncryptionPublicKeyPEM() string {
return t.encryptionPublicKeyPEM
}
2 changes: 1 addition & 1 deletion sdk/kas_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (k *KASClient) getRewrapRequest(keyAccess KeyAccess, policy string) (*kas.R
requestBody := rewrapRequestBody{
Policy: policy,
KeyAccess: keyAccess,
ClientPublicKey: k.accessTokenSource.DPoPPublicKeyPEM(),
ClientPublicKey: k.accessTokenSource.EncryptionPublicKeyPEM(),
}
requestBodyJSON, err := json.Marshal(requestBody)
if err != nil {
Expand Down