Magazine > Issues > 2007 > April >  CLR Inside Out: New Library Classes in "Orcas"
CLR Inside Out
New Library Classes in "Orcas"
Mike Downen and Inbar Gazit and Justin Van Patten

For the next version of the Microsoft .NET Framework (which will ship with the next release of Visual Studio® currently code-named "Orcas"), its assemblies have been divided into two groups, internally referred to as "red bits" and "green bits." The red bits include all the libraries that shipped before as part of the .NET Framework 2.0 and 3.0 (such as mscorlib.dll and system.dll). To maintain a high assurance of backward compatibility for Visual Studio "Orcas," changes in the red bits have been greatly limited.
The green bits assemblies are the brand new libraries with additional classes that work on top of the red bits assemblies. Most of the classes listed in this column are in the green bits assemblies (such as system.core.dll), with a few involving limited changes in the red bits assemblies. For more background information, check out Soma Somasegar's blog and Jason Zander's blog.
The classes described in this column are available as part of the January 2007 Visual Studio "Orcas" Community Technology Preview (CTP). The CLR's contributions to the new libraries include:
  • A new add-in hosting model, which was discussed in the last two editions of CLR Inside Out
  • Support for the Suite B set of cryptographic algorithms, as specified by the National Security Agency (NSA)
  • Support for big integers
  • A high-performance set collection
  • Support for anonymous and named pipes
  • Improved time zone support
  • Lightweight reader/writer lock classes
  • Better integration with Event Tracing for Windows® (ETW), including ETW provider and ETW trace listener APIs
In this column, we'll discuss the new Suite B cryptography features, big integer support, the set class, and the pipes capabilities.

Suite B Support
One of our major goals for Visual Studio "Orcas" was to add support for the Suite B set of cryptographic algorithms. In order to support Suite B, a platform has to support:
  • The Advanced Encryption Standard (AES) with key sizes of 128 and 256 bits for encryption
  • The Secure Hash Algorithm (SHA-256 and SHA-384) for hashing
  • The Elliptic Curve Digital Signature Algorithm (ECDSA) using curves of 256-bit and 384-bit prime moduli for signing
  • Elliptic Curve Diffie-Hellman (ECDH) using curves of 256 and 384-bit prime moduli for key exchange/secret agreement
More information about Suite B is available in the overview published by the NSA at www.nsa.gov/ia/industry/crypto_suite_b.cfm.
In addition to these criteria, we also wanted to support Federal Information Processing Standard (FIPS) certified implementations of these algorithms. We've had support for AES (via our RijndaelManaged class) and SHA-256 and SHA-384 (using our SHA256Managed and SHA384Managed classes) since version 1.0 of the .NET Framework, but our implementations were not FIPS-certified.
As a first step, we added managed code wrappers for the FIPS-certified AES, SHA-256, and SHA-384 implementations in Windows. In Visual Studio "Orcas," these appear as the new AesCryptoServiceProvider, SHA256CryptoServiceProvider, and SHA384CryptoServiceProvider classes. All of these are located in the System.Security.Cryptography namespace.
These algorithms are available on all platforms on which the underlying Windows Crypto Service Providers are available-Windows XP and later for AES, and Windows Server® 2003 and later for the SHA algorithms. They generally work exactly like their managed equivalents, so developers should typically be able to swap them in without noticing any changes in behavior. The one difference is that, per the AES specification, AesCryptoServiceProvider only supports a fixed block size of 128 bits.
The CngKey Class
To support our new CNG-based managed cryptography classes, we've added a CngKey class to abstract the storage and usage of CNG keys. CNG keys work similarly to key containers in today's Crypto API (CAPI)—they allow you to store a key pair or a public key securely and refer to it using a simple string name. You can use CngKey objects when working with the ECDsaCng and ECDiffieHellmanCng classes.
You can also use the CngKey class directly to perform many operations, including opening, creating, deleting, and exporting keys. If we don't have a managed API for the operation you want, you can get to the underlying key handle to use when calling Win32® APIs directly.


Elliptic Curve Cryptography
Then we added classes for ECDSA and ECDH. These classes wrap the new Crypto Next Generation (CNG) implementations of these algorithms in Windows Vista™. (For more details on how we support CNG classes, see the sidebar "The CngKey Class.") Elliptic curve cryptography is asymmetric, meaning it uses a public and private key pair for operations, similar to RSA. But elliptic curve algorithms are generally more efficient computationally than traditional public key cryptography algorithms, especially as key sizes grow larger. In fact, the NSA recommends an RSA key size of 15,360 bits to protect a symmetric key of 256 bits, whereas it recommends just a 521-bit elliptic curve key to protect the same 256-bit symmetric key.
More information on the benefits of elliptic curve cryptography is available in the NSA's article "The Case for Elliptic Key Cryptography".

ECDSA
ECDSA works like most signing algorithms: you sign with a private key and verify with a public key. The new ECDsaCng class contains our implementation of ECDSA and follows the same pattern as other managed cryptography classes. Say you want to sign some data, assuming your key pair is stored in a CNG key named MyKey:
// Returns the signature
byte[] SignMyData (byte[] data)
{
   ECDsaCng signingAlg = new ECDsaCng(CngKey.Open("MyKey"));
   return signingAlg.SignData(data);
}
Now, say you want to verify a signature from Jane and her public key is stored in a CNG key named JanesKey. You would do the following:
// Returns whether or not the signature could be verified
bool VerifyJanesSignature(byte[] data, byte[] signature)
{
   ECDsaCng signingAlg = new ECDsaCng(CngKey.Open("JanesKey"));
   return signingAlg.VerifyData(data, signature);
}
The ECDsaCng class also lets you set different options when signing or verifying, mostly through properties on the class. For example, by default the ECDsaCng class uses the SHA-256 hashing algorithm during signing and verification. If you want to use a different algorithm, you simply change the HashAlgorithm property before signing. Let's say you choose to use SHA-512. The signing example for MyKey would now look like this:
// Returns the signature
byte[] SignMyData (byte[] data)
{
   ECDsaCng signingAlg = new ECDsaCng(CngKey.Open("MyKey"));
   signingAlg.HashAlgorithm = CngAlgorithm.Sha512;
   return signingAlg.SignData(data);
}
And the verification example for JanesKey would be this:
// Returns whether or not the signature could be verified
bool VerifyJanesSignature(byte[] data, byte[] signature)
{
   ECDsaCng signingAlg = new ECDsaCng(CngKey.Open("JanesKey"));
   signingAlg.HashAlgorithm = CngAlgorithm.Sha512;
   return signingAlg.VerifyData(data, signature);
}

ECDH
ECDH is a key exchange algorithm, sometimes referred to as a secret agreement algorithm. This algorithm is used to derive a secret key-often used for encryption or decryption with a symmetric algorithm-by exchanging a public key with another party. For example, say two users, Alice and Bob, want to derive a secret key. They both already have an elliptic curve key pair (both public and private keys). In order to derive a secret key using ECDH, they would perform the following steps:
  1. Alice sends her public key to Bob. He must be sure he's really getting the key from Alice and that the key has not been modified, but the key does not have to be sent confidentially.
  2. Bob sends his public key to Alice. Likewise, Alice must be absolutely sure that she's really getting the key from Bob and that the key has not been modified, but again the key does not have to be sent confidentially.
  3. Alice and Bob can now generate the same secret value, given that they each have the other party's public key and their own key pair. This secret value can then be used to generate a secret key for a symmetric algorithm.
The code for this is pretty simple. The following example generates a 256-bit key for Alice, assuming that her key pair is stored in a CNG key named MyKey:
// Returns a 256 bit secret key that is shared with the other party
bool Generate256BitKey(string otherParty)
{
   ECDiffieHellmanCng keyExchAlg =
      new ECDiffieHellmanCng(CngKey.Open("MyKey"));
   byte [] myPublicKey = keyExchAlg.PublicKey.ToByteArray();
   ... // send myPublicKey to other party
   byte[] otherPartysPublicKeyBytes = ...; // get other party's key
   ECDiffieHellmanCngPublicKey otherPartysPublicKey =
      ECDiffieHellmanCngPublicKey.FromByteArray(
         otherPartysPublicKeyBytes, CngKeyBlobFormat.EccPublicBlob);
   return keyExchAlg.DeriveKeyMaterial(otherPartysPublicKey);
}
Alice now has a 256-bit symmetric key that can be used with the other party (Bob) to encrypt and communicate confidential data. I'll stress again, however, that it's important to make sure you're really getting the genuine public key from the other party in a situation like the example above, not from an imposter. This typically requires some form of authentication.
Like the ECDsaCng class, the ECDiffieHellmanCng class used here can be customized through properties. For example, you can use different key derivation functions specifying all of the parameters for the key derivation functions using these properties.

Big Integer Support
Support for integers that can represent very large numbers is another customer request that we have implemented in the next version of the .NET Framework. Have you ever needed a really huge number, one that is so big that it just couldn't fit into any other base-type in the .NET Framework? Now you can use the new BigInteger type, which allows you to use integers of any arbitrary size, up to the limits of available memory. To begin, let's take a look at some scenarios in which you might need to use a really big number.
Astronomical calculations that involve distances in space require the ability to calculate extremely large numbers. Astronomers often calculate the distance in light-years in order to avoid using really big numbers. That's because a light-year is about 5,878,625,373,183.61 miles (or 9,460,730,472,580.8 kilometers) and so the actual number of miles between stars (not to mention feet or inches) is larger than what can be represented with 64 bits.
Big integers are also used in statistics and number theory. While not all mathematicians are necessarily working with integers of this size, consider the Law of Truly Large Numbers (which states that with a sample size large enough, any outrageous thing is likely to happen) and some of the writings by John Edensor Littlewood.
Furthermore, many cryptographic algorithms require the use of very large prime numbers. These numbers are much larger than can be represented by base types today.
The new BigInteger class is in the new System.Numeric namespace. It supports most of the operators you'd expect of a standard numeric type, such as addition (+), subtraction (-), and multiplication (*). The sample code in Figure 1 shows how to do a factorial computation using the BigInteger class. Figure 2 shows an example of the output if you enter 1000.
402387260077093773543702433923003985719374864210714632543799910
429938512398629020592044208486969404800479988610197196058631666
872994808558901323829669944590997424504087073759918823627727188
732519779505950995276120874975462497043601418278094646496291056
393887437886487337119181045825783647849977012476632889835955735
432513185323958463075557409114262417474349347553428646576611667
797396668820291207379143853719588249808126867838374559731746136
085379534524221586593201928090878297308431392844403281231558611
036976801357304216168747609675871348312025478589320767169132448
426236131412508780208000261683151027341827977704784635868170164
365024153691398281264810213092761244896359928705114964975419909
342221566832572080821333186116811553615836546984046708975602900
950537616475847728421889679646244945160765353408198901385442487
984959953319101723355556602139450399736280750137837615307127761
926849034352625200015888535147331611702103968175921510907788019
393178114194545257223865541461062892187960223838971476088506276
862967146674697562911234082439208160153780889893964518263243671
616762179168909779911903754031274622289988005195444414282012187
361745992642956581746628302955570299024324153181617210465832036
786906117260158783520751516284225540265170483304226143974286933
061690897968482590125458327168226458066526769958652682272807075
781391858178889652208164348344825993266043367660176999612831860
788386150279465955131156552036093988180612138558600301435694527
224206344631797460594682573103790084024432438465657245014402821
885252470935190620929023136493273497565513958720559654228749774
011413346962715422845862377387538230483865688976461927383814900
140767310446640259899490222221765904339901886018566526485061799
702356193897017860040811889729918311021171229845901641921068884
387121855646124960798722908519296819372388642614839657382291123
125024186649353143970137428531926649875337218940694281434118520
158014123344828015051399694290153483077644569099073152433278288
269864602789864321139083506217095002597389863554277196742822248
757586765752344220207573630569498825087968928162753848863396909
959826280956121450994871701244516461260379029309120889086942028
510640182154399457156805941872748998094254742173582401063677404
595741785160829230135358081840096996372524230560855903700624271
243416909004153690105933983835777939410970027753472000000000000
000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000


using System.Numeric;

static BigInteger Factorial(BigInteger x)
{
    BigInteger result = 1;
    for (BigInteger i = 2; i <= x; i++)
        result *= i;
    return result;
}

static void Main(string[] args)
{
   BigInteger x = BigInteger.Parse(Console.ReadLine());
   Console.WriteLine(Factorial(x).ToString());
}

BigInteger supports many other mathematical operations through static methods on the class. These include:
  • Abs (returns the absolute value for a given BigInteger)
  • Compare (compares two BigIntegers)
  • Divide (returns the quotient of two BigIntegers)
  • DivRem (returns both the quotient and the remainder of two BigIntegers)
  • Equals (returns true if two BigIntegers have the same value)
  • GreatestCommonDivisor (returns the maximum number that is a divisor of both BigIntegers)
  • ModPow (returns one BigInteger raised to the power of another BigInteger modulo a third; the exponent cannot be negative)
  • Pow (returns one BigInteger raised to the power of another; the exponent cannot be negative)
  • Remainder (returns the remainder of dividing one BigInteger by another)
We will be posting more information on using and extending the BigInteger class on the BCL Team's blog at blogs.msdn.com/bclteam.
We should note that there are currently some design trade-offs in BigInteger. It was very important to get this new numeric type into the .NET Framework for Visual Studio "Orcas." Moreover, it was important that this new base-type be available early enough for other green bits features to make use of it. So we decided for this release not to fully support all the printing, formatting, and parsing that the other numeric types support. Thus, only the decimal (D), general (G), and hexadecimal (X) formats are supported.
The BigInteger class is an example of a new base type that we expect many other library types to consume for specialized applications. The next type we'll cover is a new collection that we added due to popular demand: a set class.

High-Performance Sets
The new HashSet class is a high-performance generic collection in the System.Collections.Generic namespace. It is an unordered collection that contains unique elements. HashSet implements all the standard collection methods (such as Add, Remove, and Contains) and provides several set operations (including union, intersection, and symmetric difference).
The following sample code demonstrates the use of HashSet with integers:
HashSet<int> set1 = new 
    HashSet<int>();
set1.Add(1);
set1.Add(3);
set1.Add(5);
set1.Add(3); // set1 already contains 3; it isn't added twice
// set1 contains 1,3,5

HashSet<int> set2 = new HashSet<int>(new int[] { 2, 4, 6 });
// set2 contains 2,4,6

set2.UnionWith(set1);
// set2 contains 1,2,3,4,5,6
Notice how after adding four items (1, 3, 5, and 3), set1 ends up containing only three elements (1, 3, and 5). This behavior is by design. HashSet contains only unique elements. Once an item has been added to the set, subsequent attempts to add the same item will not succeed. The Add method actually returns a Boolean value indicating whether or not the item was successfully added to the set. The example above could have been rewritten as follows:
HashSet<int> set1 = new HashSet<int>();
bool added = set1.Add(1); // added is true
added = set1.Add(3); // added is true
added = set1.Add(5); // added is true
added = set1.Add(3); // added is false
// set1 contains 1,3,5
In the first example, set2 is initialized by passing in an array of ints ({ 2, 4, 6}) to the HashSet constructor, which takes a generic IEnumerable as a parameter. If there are duplicate elements in the IEnumerable, they are ignored when initializing the HashSet. For example, if set2 had been initialized with an array containing 2, 4, 2, and 6, the set would still end up containing only three elements (2, 4, and 6) since duplicates are ignored.
The set operations provided by HashSet (UnionWith, IntersectWith, ExceptWith, and SymmetricExceptWith) don't just take another HashSet object as a parameter-they actually take a generic IEnumerable. This means that the set operations can be used with any other collection. Figure 3 shows how the set operations can work with any collection that implements the generic IEnumerable interface. As with the constructor overload that takes an IEnumerable, duplicates are ignored. (The behavior of UnionWith, IntersectWith, ExceptWith, and SymmetricExceptWith is outlined in Figure 4.)

Set Operation Description Example
UnionWith(IEnumerable<T> other); Modifies the HashSet to contain all the elements contained by both the HashSet or other. // set1 contains 1,2,3,4 // set2 contains 3,4,5,6 set1.UnionWith(set2); // set1 contains 1,2,3,4,5,6
IntersectWith(IEnumerable<T> other); Modifies the HashSet to contain all the elements contained by both the HashSet and other. // set1 contains 1,2,3,4 // set2 contains 3,4,5,6 set1.IntersectWith(set2); // set1 contains 3,4
ExceptWith(IEnumerable<T> other); Removes all elements contained by the HashSet that are contained by other. // set1 contains 1,2,3,4 // set2 contains 3,4,5,6 set1.ExceptWith(set2); // set1 contains 1,2
SymmetricExceptWith(IEnumerable<T> other); Modifies the HashSet to contain all elements contained by either the HashSet or other, but not both. // set1 contains 1,2,3,4 // set2 contains 3,4,5,6 set1.SymmetricExceptWith(set2); // set1 contains 1,2,5,6
List<int> oddNumbers = new List<int>();
oddNumbers.Add(1);
oddNumbers.Add(3);
oddNumbers.Add(5);

HashSet<int> numberSet = new HashSet<int>();
numberSet.UnionWith(oddNumbers);
// numberSet contains 1,3,5

int[] evenNumbers = { 2, 4, 6, 2, 6, 4, 2, 4, 6, 2 };
numberSet.UnionWith(evenNumbers); // duplicates are ignored
// numberSet contains 1,2,3,4,5,6

numberSet.ExceptWith(oddNumbers);
// numberSet contains 2,4,6
HashSet provides more operations than just the methods noted in the table. These include IsSubsetOf, IsProperSubsetOf, IsSupersetOf, and IsProperSupersetOf. With these, HashSet supports most of the mathematical operations that can typically be performed on sets.
As you can see, getting started using this new collection is quite simple. HashSet fits in nicely with our other generic collections and fills one of the last remaining holes in our collection APIs.

Pipes
The last classes we're going to look at in this column are the pipe classes. For Visual Studio "Orcas," we've added support for both anonymous and named pipes to the .NET Framework. Pipes are used to achieve interprocess communication (IPC) between two or more processes on the same machine or over a network. The new types, which can be found in the System.IO.Pipes namespace, expose nearly all the pipe functionality provided by Windows.
Anonymous pipes are used for communication between a parent process and a child process. These one-way pipes are unnamed and must be used locally on the same machine. Figure 5 shows the code for sending a string from a parent process to a child process.

Parent Process
 

using (Process process = new Process())
{
    process.StartInfo.FileName = "child.exe";
    using (AnonymousPipeServerStream pipeStream = 
        new AnonymousPipeServerStream(PipeDirection.Out,
            HandleInheritability.Inheritable)) 
   {
        process.StartInfo.Arguments =
            pipeStream.GetClientHandleAsString();
        process.StartInfo.UseShellExecute = false;
        process.Start();

        pipeStream.DisposeLocalCopyOfClientHandle();
        using (StreamWriter sw = new StreamWriter(pipeStream)) 
        {
            sw.AutoFlush = true;
            sw.WriteLine(Console.ReadLine());
        }
    }
    process.WaitForExit();
}


Child Process
 

using (PipeStream pipeStream =
    new AnonymousPipeClientStream(PipeDirection.In, args[0])) 
{    
    using (StreamReader sr = new StreamReader(pipeStream)) 
    {
        string temp;
        while ((temp = sr.ReadLine()) != null) 
        {
            Console.WriteLine(temp);
        }
    }
}
Named pipes provide much more functionality than anonymous pipes. For starters, they are full duplex and can be used over a network. Named pipes support multiple server instances of a single name, message-based communication, asynchronous I/O, and impersonation. Figure 6 shows how to send two strings from one process to another process using a named pipe.

Server Process
 
using (NamedPipeServerStream pipeStream =
    new NamedPipeServerStream("mypipe")) 
{
    pipeStream.WaitForConnection();
    using (StreamWriter sw = new StreamWriter(pipeStream)) 
    {
        sw.AutoFlush = true;
        sw.WriteLine(Console.ReadLine());
        sw.WriteLine(Console.ReadLine());
    }
}

Client Process
 
using (NamedPipeClientStream pipeStream =
    new NamedPipeClientStream("mypipe")) 
{
    pipeStream.Connect();
    using (StreamReader sr = new StreamReader(pipeStream)) 
    {
        string temp;
        while ((temp = sr.ReadLine()) != null) {
            Console.WriteLine(temp);
        }
    }
}
Named pipes also support message-based communication (demonstrated in Figure 7). This allows a reading process to read varying-length messages sent by the writing process. In Figure 7, two string messages are sent from a server process to a client process. The example includes two helper classes-MessageWriter and MessageReader-that do the actual work of writing and reading string messages to and from the underlying named pipe streams.

Helper Classes
 
class MessageWriter 
{
    NamedPipeServerStream m_pipeStream;
    Encoding m_encoding;
    public MessageWriter(NamedPipeServerStream pipeStream,
        Encoding encoding) 
    {
        m_pipeStream = pipeStream;
        m_encoding = encoding;
    }
    public void WriteMessage(string message) 
    {
        Byte[] buffer;
        buffer = m_encoding.GetBytes(message);
        m_pipeStream.Write(buffer, 0, buffer.Length);
    }
}
class MessageReader 
{
    NamedPipeClientStream m_pipeStream;
    Decoder m_decoder;
    Byte[] m_buffer = new Byte[10];
    Char[] m_charBuffer = new Char[10];
    StringBuilder m_stringBuilder = new StringBuilder();
    public MessageReader(NamedPipeClientStream pipeStream,
        Encoding encoding) 
    {
        m_pipeStream = pipeStream;
        m_decoder = encoding.GetDecoder();
    }
    public string ReadMessage() 
    {
        // Clear the StringBuilder
        m_stringBuilder.Length = 0;
        // Read the message
        int bytesRead;
        do {
            bytesRead = m_pipeStream.Read(m_buffer, 0, m_buffer.Length);
            int numChars = m_decoder.GetChars(m_buffer, 0, bytesRead,
                m_charBuffer, 0);
            m_stringBuilder.Append(m_charBuffer, 0, numChars);
        } while (!m_pipeStream.IsMessageComplete);
        // Return the message or null if the pipe has been closed
        if (bytesRead != 0) {
            m_decoder.Reset();
            return m_stringBuilder.ToString();
        }
        else return null;
    }
}

Server Process
 
using (NamedPipeServerStream pipeStream =
    new NamedPipeServerStream("messagepipe",
    PipeDirection.InOut, 1, PipeTransmissionMode.Message)) 
{
    pipeStream.WaitForConnection();
    MessageWriter mw = new MessageWriter(pipeStream, Encoding.UTF8);
    mw.WriteMessage("Hello World!");
    mw.WriteMessage("Named Pipes Are Cool!");
}

Client Process
 
using (NamedPipeClientStream pipeStream =
    new NamedPipeClientStream("messagepipe")) 
{
    pipeStream.Connect();
    pipeStream.ReadMode = PipeTransmissionMode.Message;
    MessageReader mr = new MessageReader(pipeStream, Encoding.UTF8);
    string message;
    while ((message = mr.ReadMessage()) != null) 
    {
        Console.WriteLine(message);
    }
}
As you can see, the new pipe types are first-class managed citizens, making it easy to achieve IPC from within managed code. Anyone familiar with streams should have no trouble getting started using pipes.

Conclusion
The classes discussed in this column are a sampling of the new green bits functionality available in Visual Studio "Orcas." Download the latest CTP at msdn2.microsoft.com/en-us/vstudio/aa700831.aspx to give these types a test drive. And stop by the BCL Team's blog to let us know what you think.

Send your questions and comments to  clrinout@microsoft.com.


Mike Downen is a Program Manager focusing on security and deployment on the CLR team. He can be reached via his blog at blogs.msdn.com/CLRSecurity.

Inbar Gazit is a Program Manger focusing on the base class libraries for the CLR team. He can be reached via e-mail at inbar@microsoft.com.

Justin Van Patten is a Program Manager for the base class libraries within the CLR team. He can be reached via the BCL team blog at blogs.msdn.com/bclteam.

Page view tracker