diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs index e3f1082cbc4..1790991457a 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs @@ -3119,7 +3119,7 @@ toMetadataEncryptedKey toMetadataEncryptedKey apiEncrypt = fst $ generateKeyForMetadata (BA.convert $ unPassphrase passphrase) Nothing where - (ApiEncryptMetadata (ApiT passphrase)) = apiEncrypt + (ApiEncryptMetadata (ApiT passphrase) _) = apiEncrypt -- When encryption is enabled we do the following: -- (a) recreate encrypted payload from chunks diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Types.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Types.hs index 7dc967a4549..58689bfc18e 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Types.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Types.hs @@ -197,6 +197,7 @@ module Cardano.Wallet.Api.Types , DRep (..) , DRepKeyHash (..) , DRepScriptHash (..) + , EncryptMetadataMethod (..) -- * API Types (Byron) , ApiByronWallet (..) @@ -1216,8 +1217,13 @@ data ApiMultiDelegationAction deriving (Eq, Generic, Show) deriving anyclass NFData -newtype ApiEncryptMetadata = ApiEncryptMetadata - { passphrase :: ApiT (Passphrase "lenient") } +data EncryptMetadataMethod = AES256CBC | ChaChaPoly1305 + deriving (Eq, Generic, Show) + deriving anyclass NFData + +data ApiEncryptMetadata = ApiEncryptMetadata + { passphrase :: ApiT (Passphrase "lenient") + , enc :: Maybe EncryptMetadataMethod } deriving (Eq, Generic, Show) deriving (FromJSON, ToJSON) via DefaultRecord ApiEncryptMetadata deriving anyclass NFData @@ -2258,6 +2264,24 @@ instance ToJSON ApiStakeKeyIndex where instance FromJSON ApiStakeKeyIndex where parseJSON val = ApiStakeKeyIndex <$> parseJSON val +instance ToJSON EncryptMetadataMethod where + toJSON AES256CBC = "base" + toJSON ChaChaPoly1305 = "chachapoly1305" +instance FromJSON EncryptMetadataMethod where + parseJSON t = + parseBase t <|> parseChaChaPoly1305 t + where + parseBase = withText "base" $ \txt -> + if txt == "base" then + pure AES256CBC + else + fail "'base' is expected." + parseChaChaPoly1305 = withText "ChaChaPoly1305" $ \txt -> + if txt == "chachapoly1305" then + pure ChaChaPoly1305 + else + fail "'chachapoly1305' is expected." + instance ToJSON ApiVoteAction where toJSON Abstain = "abstain" toJSON NoConfidence = "no_confidence" diff --git a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs index a9627b43bd2..caafc390ceb 100644 --- a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs +++ b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs @@ -3392,7 +3392,7 @@ spec = describe "SHARED_TRANSACTIONS" $ do let metadataToBeEncrypted = TxMetadataWithSchema TxMetadataNoSchema metadataRaw let encryptMetadata = - ApiEncryptMetadata $ ApiT $ Passphrase "metadata-secret" + ApiEncryptMetadata (ApiT $ Passphrase "metadata-secret") Nothing let payloadMetadata = Json [json|{ "encrypt_metadata": #{toJSON encryptMetadata}, "metadata": #{toJSON metadataToBeEncrypted} diff --git a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs index 9e34096aedb..1ac628d1193 100644 --- a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs +++ b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs @@ -5248,7 +5248,7 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do let metadataToBeEncrypted = TxMetadataWithSchema TxMetadataNoSchema metadataRaw let encryptMetadata = - ApiEncryptMetadata $ ApiT $ Passphrase "metadata-secret" + ApiEncryptMetadata (ApiT $ Passphrase "metadata-secret") Nothing let payload = Json [json|{ "encrypt_metadata": #{toJSON encryptMetadata}, "metadata": #{toJSON metadataToBeEncrypted} diff --git a/lib/wallet/test/unit/Cardano/Wallet/Api/TypesSpec.hs b/lib/wallet/test/unit/Cardano/Wallet/Api/TypesSpec.hs index a8af2cee7de..e5db7f21c93 100644 --- a/lib/wallet/test/unit/Cardano/Wallet/Api/TypesSpec.hs +++ b/lib/wallet/test/unit/Cardano/Wallet/Api/TypesSpec.hs @@ -249,6 +249,7 @@ import Cardano.Wallet.Api.Types , DRep (..) , DRepKeyHash (..) , DRepScriptHash (..) + , EncryptMetadataMethod (..) , Iso8601Time (..) , KeyFormat (..) , NtpSyncingStatus (..) @@ -1988,8 +1989,13 @@ instance Arbitrary TxMetadataWithSchema where <$> elements [TxMetadataNoSchema, TxMetadataDetailedSchema] <*> arbitrary +instance Arbitrary EncryptMetadataMethod where + arbitrary = elements [AES256CBC, ChaChaPoly1305] + instance Arbitrary ApiEncryptMetadata where - arbitrary = ApiEncryptMetadata <$> arbitrary + arbitrary = ApiEncryptMetadata + <$> arbitrary + <*> arbitrary instance Arbitrary DRep where arbitrary = do diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index 6c462662694..6272001abd0 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -1751,17 +1751,28 @@ x-transactionMetadata: &transactionMetadata # propertyNames: # pattern: '^[0-9]+$' +x-encryptionMethod: &encryptionMethod + type: string + enum: + - base + - chachapoly1305 + x-encryptMetadata: &encryptMetadata description: | - If used then metadata in transaction is going to be encrypted - via AEAD scheme using ChaCha20 and Poly1305 (see RFC 7539). - PBKDF2 password stretching is used to get a 32-byte symmetric key. - PBKDF2 encryption using HMAC with the hash algorithm SHA512 is employed here. + If used then metadata in transaction is going to be encrypted by either + (a) base which is AES 256 using CBC mode or + (b) AEAD scheme using ChaCha20 and Poly1305 (see RFC 7539). + PBKDF2 password stretching is used to get a 32-byte secret key and 16-byte iv + needed in (a) scheme. + PBKDF2 encryption algorithm using HMAC with the hash algorithm SHA256 is employed + using 10000 iterations. Nosalt is used at this moment. Randomized version with + salt is to be added in the future. The encrypted metadata is going to be stored in blockchain as a consequence. type: object required: - passphrase properties: + enc: *encryptionMethod passphrase: *lenientPassphrase x-transactionTTL: &transactionTTL @@ -4788,6 +4799,17 @@ x-errCreatedInvalidTransaction: &errCreatedInvalidTransaction type: string enum: ['created_invalid_transaction'] +x-errInvalidMetadataDecryption: &errInvalidMetadataDecryption + <<: *responsesErr + title: invalid_metadata_decryption + properties: + message: + type: string + description: The encrypted metadata has either wrong structure, or cannot be decrypted or decoded. + code: + type: string + enum: ['invalid_metadata_decryption'] + x-errForeignTransaction: &errForeignTransaction <<: *responsesErr title: foreign_transaction @@ -5904,6 +5926,13 @@ x-responsesDecodedTransaction: &responsesDecodedTransaction content: application/json: schema: *ApiDecodedTransaction + 403: + description: Forbidden + content: + application/json: + schema: + oneOf: + - <<: *errInvalidMetadataDecryption x-responsesSubmitTransaction: &responsesSubmitTransaction <<: *responsesErr400