diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index 42ebfcf5ca..76632694d7 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -1490,6 +1490,59 @@ cleanup:
     return PSA_SUCCESS;
 }
 
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+static psa_status_t psa_key_agreement_ffdh(const uint8_t *peer_key,
+                                           size_t peer_key_length,
+                                           const uint8_t *our_key,
+                                           size_t our_key_length,
+                                           uint8_t *shared_secret,
+                                           size_t shared_secret_size,
+                                           size_t *shared_secret_length)
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+    mbedtls_mpi P, G, X, GY, K;
+    const size_t calculated_shared_secret_size = peer_key_length;
+
+    if (peer_key_length != our_key_length ||
+        calculated_shared_secret_size > shared_secret_size) {
+        return PSA_ERROR_INVALID_ARGUMENT;
+    }
+
+    mbedtls_mpi_init(&P); mbedtls_mpi_init(&G);
+    mbedtls_mpi_init(&X); mbedtls_mpi_init(&GY);
+    mbedtls_mpi_init(&K);
+
+    status = psa_ffdh_set_prime_generator(peer_key_length, &P, &G);
+
+    if (status == PSA_SUCCESS) {
+        MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, our_key,
+                                                our_key_length));
+
+        MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&GY, peer_key,
+                                                peer_key_length));
+
+        /* Calculate shared secret public key: K = G^(XY) mod P */
+        MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&K, &GY, &X, &P, NULL));
+
+        MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&K, shared_secret,
+                                                 calculated_shared_secret_size));
+
+        *shared_secret_length = calculated_shared_secret_size;
+    }
+cleanup:
+    mbedtls_mpi_free(&P); mbedtls_mpi_free(&G);
+    mbedtls_mpi_free(&X); mbedtls_mpi_free(&GY);
+    mbedtls_mpi_free(&K);
+
+    if (status == PSA_SUCCESS && ret != 0) {
+        return mbedtls_to_psa_error(ret);
+    }
+
+    return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
 static psa_status_t psa_export_ffdh_public_key_internal(
     const uint8_t *key_buffer,
     size_t key_buffer_size,
@@ -6118,6 +6171,11 @@ static psa_status_t psa_key_agreement_try_support(psa_algorithm_t alg)
     if (alg == PSA_ALG_ECDH) {
         return PSA_SUCCESS;
     }
+#endif
+#if defined(PSA_WANT_ALG_FFDH)
+    if (alg == PSA_ALG_FFDH) {
+        return PSA_SUCCESS;
+    }
 #endif
     (void) alg;
     return PSA_ERROR_NOT_SUPPORTED;
@@ -6707,6 +6765,22 @@ psa_status_t psa_key_agreement_raw_builtin(const psa_key_attributes_t *attribute
                                                   shared_secret_size,
                                                   shared_secret_length);
 #endif /* MBEDTLS_PSA_BUILTIN_ALG_ECDH */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+        case PSA_ALG_FFDH:
+            if (!PSA_KEY_TYPE_IS_DH_KEY_PAIR(psa_get_key_type(attributes))) {
+                return PSA_ERROR_INVALID_ARGUMENT;
+            }
+
+            return psa_key_agreement_ffdh(peer_key,
+                                          peer_key_length,
+                                          key_buffer,
+                                          key_buffer_size,
+                                          shared_secret,
+                                          shared_secret_size,
+                                          shared_secret_length);
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
         default:
             (void) attributes;
             (void) key_buffer;