The goal of program obfuscation is to "scramble" a computer program, hiding its implementation details (making it hard to "reverse-engineer"), while preserving the functionality (i.e, input/output behavior) of the program: an obfuscator O is a compiler which takes a programC and compiles it to a program C' = O(C) that has exactly the same functionality as C (i.e., they provide the same output on every input), yet the code of C' is "unintelligible". Program obfuscation is widely used in practice--for example, in applications such as Skype or Instagram, it permits distributing code to a huge number of users, enabling them to communicate and perform large-scale distributed computations, while ensuring the users only employ the service in the intended way. However, these usages largely rely on ad-hoc heuristics that often get broken (for instance, a google search reveals multiple practical methods for reverse-engineering Instagram, exposing all secret keys used by the obfuscated Instagram clients). Rather, similar to the modern study of Cryptography, it would be desirable to put the study of program obfuscation under a rigorous mathematical treatment by precisely defining its security properties, precisely defining some computational intractability assumptions (e.g., the hardness of factoring products of large primes), and proving that any attack on the obfuscation must violate the computational assumptions. The principal goals of our original proposal was a) to provide such a treatment, and b) more generally, studying techniques for ensuring that large-scale distributed services can only be employed in their intended way. During this reporting period we have made progress on both of these directions, but we here mostly focus on a).