Skip to content

Commit

Permalink
Use pointer and methods
Browse files Browse the repository at this point in the history
  • Loading branch information
MicahParks committed Nov 21, 2023
1 parent b7b3bc6 commit d28c10a
Show file tree
Hide file tree
Showing 12 changed files with 331 additions and 295 deletions.
36 changes: 22 additions & 14 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,30 @@ const (
// AlgEdDSA is the EdDSA algorithm.
AlgEdDSA ALG = "EdDSA"

// KeyOpsSign is a key operation for signing.
// KeyOpsSign is a Key operation for signing.
KeyOpsSign = "sign"
// KeyOpsVerify is a key operation for verifying.
// KeyOpsVerify is a Key operation for verifying.
KeyOpsVerify = "verify"
// KeyOpsEncrypt is a key operation for encryption.
// KeyOpsEncrypt is a Key operation for encryption.
KeyOpsEncrypt = "encrypt"
// KeyOpsDecrypt is a key operation for decryption.
// KeyOpsDecrypt is a Key operation for decryption.
KeyOpsDecrypt = "decrypt"
// KeyOpsWrapKey is a key operation for wrapping a key.
// KeyOpsWrapKey is a Key operation for wrapping a Key.
KeyOpsWrapKey = "wrapKey"
// KeyOpsUnwrapKey is a key operation for unwrapping a key.
// KeyOpsUnwrapKey is a Key operation for unwrapping a Key.
KeyOpsUnwrapKey = "unwrapKey"
// KeyOpsDeriveKey is a key operation for deriving a key.
// KeyOpsDeriveKey is a Key operation for deriving a Key.
KeyOpsDeriveKey = "deriveKey"
// KeyOpsDeriveBits is a key operation for deriving bits.
// KeyOpsDeriveBits is a Key operation for deriving bits.
KeyOpsDeriveBits = "deriveBits"

// KtyEC is the key type for ECDSA.
// KtyEC is the Key type for ECDSA.
KtyEC KTY = "EC"
// KtyOKP is the key type for EdDSA.
// KtyOKP is the Key type for EdDSA.
KtyOKP KTY = "OKP"
// KtyRSA is the key type for RSA.
// KtyRSA is the Key type for RSA.
KtyRSA KTY = "RSA"
// KtyOct is the key type for octet sequences, such as HMAC.
// KtyOct is the Key type for octet sequences, such as HMAC.
KtyOct KTY = "oct"

// CrvEd25519 is a curve for EdDSA.
Expand All @@ -65,7 +65,7 @@ const (
// CrvP521 is a curve for ECDSA.
CrvP521 CRV = "P-521"

// HeaderKID is a JWT header for the key ID.
// HeaderKID is a JWT header for the Key ID.
HeaderKID = "kid"

// UseEnc indicates a JWK is for encryption.
Expand Down Expand Up @@ -115,6 +115,14 @@ func (kty KTY) String() string {
return string(kty)
}

func (kty KTY) valid() bool {
switch kty {
case KtyEC, KtyOKP, KtyRSA, KtyOct:
return true
}
return false
}

// USE is a set of "JSON Web Key Use" types from https://www.iana.org/assignments/jose/jose.xhtml as mentioned in
// https://www.rfc-editor.org/rfc/rfc7517#section-4.2
type USE string
Expand All @@ -125,7 +133,7 @@ func (use USE) String() string {

func (use USE) valid() bool {
switch use {
case UseEnc, UseSig:
case UseEnc, UseSig, "":
return true
}
return false
Expand Down
21 changes: 10 additions & 11 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,29 @@ var (
errStorage = errors.New("storage error")
)

type storageError[CustomKeyMeta any] struct{}
var _ jwkset.Storage = &storageError{}

func (s storageError[CustomKeyMeta]) DeleteKey(ctx context.Context, keyID string) (ok bool, err error) {
type storageError struct{}

func (s storageError) DeleteKey(_ context.Context, _ string) (ok bool, err error) {
return false, errStorage
}

func (s storageError[CustomKeyMeta]) ReadKey(ctx context.Context, keyID string) (jwkset.KeyWithMeta[CustomKeyMeta], error) {
return jwkset.KeyWithMeta[CustomKeyMeta]{}, errStorage
func (s storageError) ReadKey(_ context.Context, _ string) (jwkset.JWK, error) {
return jwkset.JWK{}, errStorage
}

func (s storageError[CustomKeyMeta]) SnapshotKeys(ctx context.Context) ([]jwkset.KeyWithMeta[CustomKeyMeta], error) {
func (s storageError) SnapshotKeys(_ context.Context) ([]jwkset.JWK, error) {
return nil, errStorage
}

func (s storageError[CustomKeyMeta]) WriteKey(ctx context.Context, meta jwkset.KeyWithMeta[CustomKeyMeta]) error {
func (s storageError) WriteKey(_ context.Context, _ jwkset.JWK) error {
return errStorage
}

func TestStorageError(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

jwks := jwkset.NewMemory[any]()
jwks.Store = storageError[any]{}
jwks := jwkset.NewMemory()
jwks.Store = storageError{}

_, err := jwks.JSONPublic(ctx)
if err == nil {
Expand Down
14 changes: 12 additions & 2 deletions examples/http_server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,24 @@ func main() {
ctx := context.Background()
logger := log.New(os.Stdout, "", 0)

jwkSet := jwkset.NewMemory[any]()
jwkSet := jwkset.NewMemory()

key, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
logger.Fatalf(logFmt, "Failed to generate RSA key.", err)
}

err = jwkSet.Store.WriteKey(ctx, jwkset.NewKey[any](key, "my-key-id"))
metadata := jwkset.JWKMetadataOptions{
KID: "my-key-id",
}
options := jwkset.JWKOptions{
Metadata: metadata,
}
jwk, err := jwkset.NewJWKFromKey(key, options)
if err != nil {
logger.Fatalf(logFmt, "Failed to create JWK.", err)
}
err = jwkSet.Store.WriteKey(ctx, jwk)
if err != nil {
logger.Fatalf(logFmt, "Failed to store RSA key.", err)
}
Expand Down
53 changes: 30 additions & 23 deletions examples/individual_keys/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,46 +21,53 @@ func main() {
logger.Fatalf(logFmt, "Failed to generate EdDSA key.", err)
}

// Wrap the key in the appropriate Go type.
meta := jwkset.NewKey[any](private, "my-key-id")
// Give this EdDSA key a key ID.
metadata := jwkset.JWKMetadataOptions{
KID: "my-key-id",
}

// Create the approrpiate options to include the private key material in the JSON representation.
options := jwkset.JWKOptions{
AsymmetricPrivate: true,
// Specify options for marshalling and unmarshalling this key from JSON.
marshalOptions := jwkset.JWKMarshalOptions{
MarshalAsymmetricPrivate: true, // Required to marshal the EdDSA private key.
MarshalSymmetric: true, // Unused in this example, EdDSA is asymmetric.
UnmarshalAsymmetricPrivate: true, // Required to unmarshal the EdDSA private key.
UnmarshalSymmetric: true, // Unused in this example, EdDSA is asymmetric.
}

// Marshal the key to a different Go type that can be serialized to JSON.
marshal, err := jwkset.KeyMarshal(meta, options)
// Create the JWK from the key and provided options.
options := jwkset.JWKOptions{
Marshal: marshalOptions,
Metadata: metadata,
}
jwk, err := jwkset.NewJWKFromKey(private, options)
if err != nil {
logger.Fatalf(logFmt, "Failed to marshal key.", err)
logger.Fatalf(logFmt, "Failed to create JWK.", err)
}

// Marshal the new type to JSON.
j, err := json.MarshalIndent(marshal, "", " ")
// Marshal the JWK to JSON.
raw, err := json.MarshalIndent(jwk.Marshal(), "", " ")
if err != nil {
logger.Fatalf(logFmt, "Failed to marshal JSON.", err)
}
println(string(j))
println(string(raw))

// Unmarshal the raw JSON into a Go type that can be deserialized into a key.
err = json.Unmarshal(j, &marshal)
// Unmarshal the raw JSON into the jwkset.JWKMarshal type.
var marshal jwkset.JWKMarshal
err = json.Unmarshal(raw, &marshal)
if err != nil {
logger.Fatalf(logFmt, "Failed to unmarshal JSON.", err)
}

// Create the appropriate options to include the private key material in the deserialization.
// Use the jwkset.JWKMarshal type to create a JWK.
//
// If this option is not provided, the resulting key will be of the type ed25519.PublicKey.
unmarshalOptions := jwkset.JWKMarshalOptions{
AsymmetricPrivate: true,
}

// Convert the Go type back into a key with metadata.
meta, err = jwkset.KeyUnmarshal[any](marshal, unmarshalOptions)
// The options for JSON marshalling and unmarshalling are copied from earlier in the example.
// The default validation options have been used.
options.Marshal.UnmarshalAsymmetricPrivate = false
jwk, err = jwkset.NewJWKFromMarshal(marshal, options.Marshal, jwkset.JWKValidateOptions{})
if err != nil {
logger.Fatalf(logFmt, "Failed to unmarshal key.", err)
logger.Fatalf(logFmt, "Failed to create JWK.", err)
}

// Print the key ID.
println(meta.KeyID)
println(jwk.Marshal().KID)
}
4 changes: 2 additions & 2 deletions examples/storage_operations/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func main() {
if err != nil {
logger.Fatalf(logFmt, "Failed to read EdDSA key.", err)
}
edKey, ok := meta.Key.(ed25519.PrivateKey)
edKey, ok := meta.key.(ed25519.PrivateKey)
if !ok {
logger.Fatalf(logFmt, "Failed to cast EdDSA key.", err)
}
Expand All @@ -122,7 +122,7 @@ func main() {
if err != nil {
logger.Fatalf(logFmt, "Failed to read HMAC key.", err)
}
hKey, ok := meta.Key.([]byte)
hKey, ok := meta.key.([]byte)
if !ok {
logger.Fatalf(logFmt, "Failed to cast HMAC key.", err)
}
Expand Down
8 changes: 4 additions & 4 deletions jwk.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (j JWKSet) JSONPublic(ctx context.Context) (json.RawMessage, error) {
return j.JSONWithOptions(ctx, JWKMarshalOptions{}, JWKValidateOptions{})
}

// JSONPrivate creates the JSON representation of the JWKSet public and private key material.
// JSONPrivate creates the JSON representation of the JWKSet public and private Key material.
func (j JWKSet) JSONPrivate(ctx context.Context) (json.RawMessage, error) {
marshalOptions := JWKMarshalOptions{
MarshalAsymmetricPrivate: true,
Expand All @@ -38,7 +38,7 @@ func (j JWKSet) JSONPrivate(ctx context.Context) (json.RawMessage, error) {
return j.JSONWithOptions(ctx, marshalOptions, JWKValidateOptions{})
}

// JSONWithOptions creates the JSON representation of the JWKSet with the given options.
// JSONWithOptions creates the JSON representation of the JWKSet with the given Options.
func (j JWKSet) JSONWithOptions(ctx context.Context, marshalOptions JWKMarshalOptions, validationOptions JWKValidateOptions) (json.RawMessage, error) {
jwks := JWKSMarshal{}

Expand All @@ -51,9 +51,9 @@ func (j JWKSet) JSONWithOptions(ctx context.Context, marshalOptions JWKMarshalOp
options := key.options
options.Marshal = marshalOptions
options.Validate = validationOptions
marshal, err := keyMarshal(key.Key(), options)
marshal, err := keyMarshal(key.key, options)
if err != nil {
return nil, fmt.Errorf("failed to marshal key: %w", err)
return nil, fmt.Errorf("failed to marshal Key: %w", err)
}
jwks.Keys = append(jwks.Keys, marshal)
}
Expand Down
18 changes: 9 additions & 9 deletions jwk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,38 @@ func TestJSON(t *testing.T) {
block, _ := pem.Decode([]byte(ecPrivateKey))
eKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
t.Fatalf("Failed to parse EC private key. %s", err)
t.Fatalf("Failed to parse EC private Key. %s", err)
}
const eID = "myECKey"
err = jwks.Store.WriteKey(ctx, jwkset.NewKey[any](eKey.(*ecdsa.PrivateKey), eID))
if err != nil {
t.Fatalf("Failed to write EC key. %s", err)
t.Fatalf("Failed to write EC Key. %s", err)
}

edPriv, err := base64.RawURLEncoding.DecodeString(edPrivateKey)
if err != nil {
t.Fatalf("Failed to decode EdDSA private key. %s", err)
t.Fatalf("Failed to decode EdDSA private Key. %s", err)
}
edPub, err := base64.RawURLEncoding.DecodeString(edPublicKey)
if err != nil {
t.Fatalf("Failed to decode EdDSA public key. %s", err)
t.Fatalf("Failed to decode EdDSA public Key. %s", err)
}
ed := ed25519.PrivateKey(append(edPriv, edPub...))
const edID = "myEdDSAKey"
err = jwks.Store.WriteKey(ctx, jwkset.NewKey[any](ed, edID))
if err != nil {
t.Fatalf("Failed to write EdDSA key. %s", err)
t.Fatalf("Failed to write EdDSA Key. %s", err)
}

block, _ = pem.Decode([]byte(rsaPrivateKey))
rKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
t.Fatalf("Failed to parse RSA private key. %s", err)
t.Fatalf("Failed to parse RSA private Key. %s", err)
}
const rID = "myRSAKey"
err = jwks.Store.WriteKey(ctx, jwkset.NewKey[any](rKey.(*rsa.PrivateKey), rID))
if err != nil {
t.Fatalf("Failed to write RSA key. %s", err)
t.Fatalf("Failed to write RSA Key. %s", err)
}

hKey := []byte(hmacSecret)
Expand All @@ -65,7 +65,7 @@ func TestJSON(t *testing.T) {
KeyID: hID,
})
if err != nil {
t.Fatalf("Failed to write HMAC key. %s", err)
t.Fatalf("Failed to write HMAC Key. %s", err)
}

jsonRepresentation, err := jwks.JSONPublic(ctx)
Expand Down Expand Up @@ -105,7 +105,7 @@ func compareJSON(t *testing.T, actual json.RawMessage, private bool) {
for _, key := range keys.Keys {
kty, ok := key["kty"].(string)
if !ok {
t.Fatal("Failed to get key type.")
t.Fatal("Failed to get Key type.")
}

var expectedJSON json.RawMessage
Expand Down
Loading

0 comments on commit d28c10a

Please sign in to comment.