From b0a66d5dbfa5beace6d57e602e3a61e1ac95e7ed Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 12 May 2025 14:43:04 +0200 Subject: [PATCH 1/3] Add API Metadata Encryption --- api/encryption.go | 7 +++++++ api/metadata.go | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/api/encryption.go b/api/encryption.go index c39eee3..2c393ee 100644 --- a/api/encryption.go +++ b/api/encryption.go @@ -33,12 +33,19 @@ func (c *Client) EncryptMessage(message string) (string, error) { } // EncryptMessageWithPublicKey encrypts a message using the provided public key and then signes the message using the users private key +// +// Deprecated: EncryptMessageWithPublicKey is deprecated. Use EncryptMessageWithKey instead func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, error) { publicKey, err := crypto.NewKeyFromArmored(publickey) if err != nil { return "", fmt.Errorf("Get Public Key: %w", err) } + return c.EncryptMessageWithKey(publicKey, message) +} + +// EncryptMessageWithKey encrypts a message using the provided key and then signes the message using the users private key +func (c *Client) EncryptMessageWithKey(publicKey *crypto.Key, message string) (string, error) { key, err := c.userPrivateKey.Copy() if err != nil { return "", fmt.Errorf("Get Private Key Copy: %w", err) diff --git a/api/metadata.go b/api/metadata.go index f62464f..cc40425 100644 --- a/api/metadata.go +++ b/api/metadata.go @@ -68,3 +68,14 @@ func (c *Client) DecryptMetadata(metadataKey *crypto.Key, armoredCiphertext stri return metadata, nil } + +func (c *Client) EncryptMetadata(metadataKey *crypto.Key, data string) (string, error) { + armoredCiphertext, err := c.EncryptMessageWithKey(metadataKey, data) + if err != nil { + return "", fmt.Errorf("Encrypting Metadata: %w", err) + } + + // TODO save Session Key to cache + + return armoredCiphertext, nil +} From eb551646967293fdc7b828988ecf1f2b0bc21ded Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 12 May 2025 14:44:51 +0200 Subject: [PATCH 2/3] Add Fallback Resource Json Schemas --- api/schema.go | 231 +++++++++++++++++++++++++++++++++++++++++++++ api/schema_test.go | 18 ++++ 2 files changed, 249 insertions(+) create mode 100644 api/schema.go create mode 100644 api/schema_test.go diff --git a/api/schema.go b/api/schema.go new file mode 100644 index 0000000..b9fe6d4 --- /dev/null +++ b/api/schema.go @@ -0,0 +1,231 @@ +package api + +import "encoding/json" + +// Fallback Schema, Only to be used if we encounter a Broken Server (v5.0), Not API Stable! +var ResourceSchemas = map[string]json.RawMessage{ + "v5-default": json.RawMessage(` +{ + "resource": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "username": { + "type": "string", + "maxLength": 255, + "nullable": true + }, + "uris": { + "type": "array", + "items": { + "type": "string", + "maxLength": 1024, + "nullable": true + } + }, + "description": { + "type": "string", + "maxLength": 10000, + "nullable": true + } + } + }, + "secret": { + "type": "object", + "required": ["password"], + "properties": { + "object_type": { + "type": "string", + "enum": ["PASSBOLT_SECRET_DATA"] + }, + "password": { + "type": "string", + "maxLength": 4096, + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 10000, + "nullable": true + } + } + } +}`), + "v5-password-string": json.RawMessage(` +{ + "resource": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "username": { + "type": "string", + "maxLength": 255, + "nullable": true + }, + "uris": { + "type": "array", + "items": { + "type": "string", + "maxLength": 1024, + "nullable": true + } + }, + "description": { + "type": "string", + "maxLength": 10000, + "nullable": true + } + } + }, + "secret": { + "type": "string", + "maxLength": 4096 + } +}`), + "v5-default-with-totp": json.RawMessage(` +{ + "resource": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "username": { + "type": "string", + "maxLength": 255, + "nullable": true + }, + "uris": { + "type": "array", + "items": { + "type": "string", + "maxLength": 1024, + "nullable": true + } + }, + "description": { + "type": "string", + "maxLength": 10000, + "nullable": true + } + } + }, + "secret": { + "type": "object", + "required": ["totp"], + "properties": { + "object_type": { + "type": "string", + "enum": ["PASSBOLT_SECRET_DATA"] + }, + "password": { + "type": "string", + "maxLength": 4096, + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 10000, + "nullable": true + }, + "totp": { + "type": "object", + "required": ["secret_key", "digits", "algorithm"], + "properties": { + "algorithm": { + "type": "string", + "minLength": 4, + "maxLength": 6 + }, + "secret_key": { + "type": "string", + "maxLength": 1024 + }, + "digits": { + "type": "number", + "minimum": 6, + "maximum": 8 + }, + "period": { + "type": "number" + } + } + } + } + } +}`), + "v5-totp-standalone": json.RawMessage(` +{ + "resource": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "username": { + "type": "string", + "maxLength": 255, + "nullable": true + }, + "uris": { + "type": "array", + "items": { + "type": "string", + "maxLength": 1024, + "nullable": true + } + }, + "description": { + "type": "string", + "maxLength": 10000, + "nullable": true + } + } + }, + "secret": { + "type": "object", + "required": ["totp"], + "properties": { + "object_type": { + "type": "string", + "enum": ["PASSBOLT_SECRET_DATA"] + }, + "totp": { + "type": "object", + "required": ["secret_key", "digits", "algorithm"], + "properties": { + "algorithm": { + "type": "string", + "minLength": 4, + "maxLength": 6 + }, + "secret_key": { + "type": "string", + "maxLength": 1024 + }, + "digits": { + "type": "number", + "minimum": 6, + "maximum": 8 + }, + "period": { + "type": "number" + } + } + } + } + } +}`), +} diff --git a/api/schema_test.go b/api/schema_test.go new file mode 100644 index 0000000..a9069e4 --- /dev/null +++ b/api/schema_test.go @@ -0,0 +1,18 @@ +package api + +import ( + "encoding/json" + "testing" +) + +func TestResourceJsonSchema(t *testing.T) { + for slug, schema := range ResourceSchemas { + var schemaDefinition ResourceTypeSchema + err := json.Unmarshal(schema, &schemaDefinition) + if err != nil { + t.Errorf("Error While Parsing Resource Schema %v: %v", slug, err) + } else { + t.Logf("Schema for type %v is ok", slug) + } + } +} From 91df259a870250024d21d964b0af3f31d0c0f07e Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Mon, 12 May 2025 14:49:13 +0200 Subject: [PATCH 3/3] Use Fallback Schemas for Metadata --- helper/metadata.go | 77 ++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/helper/metadata.go b/helper/metadata.go index eebb89e..e89cb78 100644 --- a/helper/metadata.go +++ b/helper/metadata.go @@ -1,11 +1,14 @@ package helper import ( + "bytes" "context" "encoding/json" "fmt" + "strings" "github.com/passbolt/go-passbolt/api" + "github.com/santhosh-tekuri/jsonschema" ) func GetResourceMetadata(ctx context.Context, c *api.Client, resource api.Resource, rType api.ResourceType) (string, error) { @@ -54,47 +57,53 @@ func GetResourceMetadata(ctx context.Context, c *api.Client, resource api.Resour if err != nil { return "", fmt.Errorf("Decrypt Metadata: %w", err) } - /* - - var schemaDefinition api.ResourceTypeSchema - err = json.Unmarshal([]byte(rType.Definition), &schemaDefinition) - if err != nil { - // Workaround for inconsistant API Responses where sometime the Schema is embedded directly and sometimes it's escaped as a string - if err.Error() == "json: cannot unmarshal string into Go value of type api.ResourceTypeSchema" { - var tmp string - err = json.Unmarshal([]byte(rType.Definition), &tmp) - if err != nil { - return "", fmt.Errorf("Workaround Unmarshal Json Schema String: %w", err) - } - - err = json.Unmarshal([]byte(tmp), &schemaDefinition) - if err != nil { - return "", fmt.Errorf("Workaround Unmarshal Json Schema: %w", err) - } - - } else { - return "", fmt.Errorf("Unmarshal Json Schema: %w", err) + var schemaDefinition api.ResourceTypeSchema + err = json.Unmarshal([]byte(rType.Definition), &schemaDefinition) + if err != nil { + // Workaround for inconsistant API Responses where sometime the Schema is embedded directly and sometimes it's escaped as a string + if err.Error() == "json: cannot unmarshal string into Go value of type api.ResourceTypeSchema" { + var tmp string + err = json.Unmarshal([]byte(rType.Definition), &tmp) + if err != nil { + return "", fmt.Errorf("Workaround Unmarshal Json Schema String: %w", err) } - } - comp := jsonschema.NewCompiler() + if tmp == "[]" { + // Use The Builtin Fallback Schemas in this Case + schema, ok := api.ResourceSchemas[rType.Slug] + if !ok { + return "", fmt.Errorf("Server Does not have the Required json Schema and there is no fallback available for type: %v", rType.Slug) + } + tmp = string(schema) + } - err = comp.AddResource("metadata.json", bytes.NewReader(schemaDefinition.Secret)) - if err != nil { - return "", fmt.Errorf("Adding Json Schema: %w", err) - } + err = json.Unmarshal([]byte(tmp), &schemaDefinition) + if err != nil { + return "", fmt.Errorf("Workaround Unmarshal Json Schema: %w", err) + } - schema, err := comp.Compile("metadata.json") - if err != nil { - return "", fmt.Errorf("Compiling Json Schema: %w", err) + } else { + return "", fmt.Errorf("Unmarshal Json Schema: %w", err) } + } - err = schema.Validate(strings.NewReader(decMetadata)) - if err != nil { - return "", fmt.Errorf("Validating Secret Data: %w", err) - } - */ + comp := jsonschema.NewCompiler() + + err = comp.AddResource("metadata.json", bytes.NewReader(schemaDefinition.Resource)) + if err != nil { + return "", fmt.Errorf("Adding Json Schema: %w", err) + } + + schema, err := comp.Compile("metadata.json") + if err != nil { + return "", fmt.Errorf("Compiling Json Schema: %w", err) + } + + err = schema.Validate(strings.NewReader(decMetadata)) + if err != nil { + return "", fmt.Errorf("Validating Secret Data: %w", err) + } return decMetadata, nil }