Your problem is transmission of a secret piece of data; that the piece of data is a cryptographic key is mostly irrelevant (it just means that it is really secret). Thus, what you want boils down to making sure of who gets it.
Since you talk about processes, I assume you are talking about a transfer within a single machine. "Who" then depends on the security model of your operating system. Common OS use a model with "accounts", each process being linked to one such accounts and having the privileges associated with that account. There is one "master account" (root on Unix, Administrator on Windows) which has all the rights, and you cannot defend against that.
Given that, let's see your proposals:
- "As a return value": this cannot apply. A "return value" is what is sent by a function to its caller within a single process. In your case, there are two processes.
- "Written to a file": I would advise against it. A file leaves traces. Once the data has made it to a physical medium, then you must maintain the access controls to this medium forever. It seems best to keep the whole thing in RAM as much as possible.
- "Encryption and a socket": encryption is fine as long as you already have keys distributed, so you are solving the chicken-and-egg problem by stating that you already have another egg/chicken, which does not solve things. On the other hand, for a socket which is local to a machine, you may have ways to make sure that the process at the other end of the socket is owned by the expected account; it is called
getpeereid() on Unix-like systems.
- "Inter-Process Communications": this is a generic name for ways for two processes to talk to each other, and it includes local sockets. The choice is large (and very OS-dependent); the important point being that you can obtain the effective user ID of the other process, as
getpeereid() does.
On a Unix-like system, you can use a local socket, either anonymous (created with socketpair()) or named (a "Unix domain socket", as it is); an anonymous socket pair is possible if your two processes have a father-son relationship which allowed them to exchange file descriptors. You can also use pipes, there again anonymous, or through a file-based rendez-vous point (a "named pipe"), in which the filesystem contains the meeting area, but the data remains purely in RAM. SysV shared memory, mmap() with MAP_SHARED over /dev/zero,... there are many possibilities, each with its own methods for access controls.
On a Windows system, the possibilities are even more varied, due to the long history of successive layers of inventiveness in this OS (they never drop anything -- they are good at backward compatibility -- but they also redesign whole subsystems every two years, so the IPC methods tend to accumulate; back in 2003, I counted no less than 9 methods for that, and I am sure there are now others).