File: Purpose.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (166 lines) | stat: -rw-r--r-- 10,046 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//------------------------------------------------------------------------------
// <copyright file="Purpose.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------

namespace System.Web.Security.Cryptography {
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;

    // Represents a purpose that can be passed to a cryptographic routine to control key derivation / ciphertext modification.
    // This is hardening the crypto routines to prevent playing ciphertext off of components that didn't generate them.
    //
    // !! IMPORTANT !!
    // The built-in purposes do not contain privileged information and are not meant to be treated as secrets. Any external
    // person can disassemble our code or look directly at our source to see what our Purpose objects are used for.
    //
    // PrimaryPurpose: This is a well-known string that identifies the reason for this Purpose. The pattern we use
    // is that PrimaryPurpose is the name of the consumer, making each consumer's Purpose unique.
    //
    // SpecificPurposes: These are extra optional strings that further differentiate Purpose objects that might have the
    // same PrimaryPurpose. The pattern we use is that if a single consumer has multiple Purposes, he can use
    // SpecificPurposes to uniquely identify them. The information here is generally not secret (we can put the type of
    // the currently executing Page here, for example), but it is valid to seed this property with a secret obtained
    // at runtime (such as a nonce shared between two parties).

    internal sealed class Purpose {

        // predefined purposes
        public static readonly Purpose AnonymousIdentificationModule_Ticket = new Purpose("AnonymousIdentificationModule.Ticket");
        public static readonly Purpose AssemblyResourceLoader_WebResourceUrl = new Purpose("AssemblyResourceLoader.WebResourceUrl");
        public static readonly Purpose FormsAuthentication_Ticket = new Purpose("FormsAuthentication.Ticket");
        public static readonly Purpose WebForms_Page_PreviousPageID = new Purpose("WebForms.Page.PreviousPageID");
        public static readonly Purpose RolePrincipal_Ticket = new Purpose("RolePrincipal.Ticket");
        public static readonly Purpose ScriptResourceHandler_ScriptResourceUrl = new Purpose("ScriptResourceHandler.ScriptResourceUrl");

        // predefined ViewState purposes; they won't be used as-is (they're combined with the page information)
        public static readonly Purpose WebForms_ClientScriptManager_EventValidation = new Purpose("WebForms.ClientScriptManager.EventValidation");
        public static readonly Purpose WebForms_DetailsView_KeyTable = new Purpose("WebForms.DetailsView.KeyTable");
        public static readonly Purpose WebForms_GridView_DataKeys = new Purpose("WebForms.GridView.DataKeys");
        public static readonly Purpose WebForms_GridView_SortExpression = new Purpose("WebForms.GridView.SortExpression");
        public static readonly Purpose WebForms_HiddenFieldPageStatePersister_ClientState = new Purpose("WebForms.HiddenFieldPageStatePersister.ClientState");
        public static readonly Purpose WebForms_ScriptManager_HistoryState = new Purpose("WebForms.ScriptManager.HistoryState");
        public static readonly Purpose WebForms_SessionPageStatePersister_ClientState = new Purpose("WebForms.SessionPageStatePersister.ClientState");

        // predefined miscellaneoous purposes; they won't be used as-is (they're combined with other specificPurposes)
        public static readonly Purpose User_MachineKey_Protect = new Purpose("User.MachineKey.Protect"); // used by the MachineKey static class Protect / Unprotect methods
        public static readonly Purpose User_ObjectStateFormatter_Serialize = new Purpose("User.ObjectStateFormatter.Serialize"); // used by ObjectStateFormatter.Serialize() if called manually

        public readonly string PrimaryPurpose;
        public readonly string[] SpecificPurposes;

        private byte[] _derivedKeyLabel;
        private byte[] _derivedKeyContext;

        public Purpose(string primaryPurpose, params string[] specificPurposes)
            : this(primaryPurpose, specificPurposes, null, null) {
        }

        // ctor for unit testing
        internal Purpose(string primaryPurpose, string[] specificPurposes, CryptographicKey derivedEncryptionKey, CryptographicKey derivedValidationKey) {
            PrimaryPurpose = primaryPurpose;
            SpecificPurposes = specificPurposes ?? new string[0];
            DerivedEncryptionKey = derivedEncryptionKey;
            DerivedValidationKey = derivedValidationKey;
            SaveDerivedKeys = (SpecificPurposes.Length == 0);
        }

        // The cryptographic keys that were derived from this Purpose.
        internal CryptographicKey DerivedEncryptionKey { get; private set; }
        internal CryptographicKey DerivedValidationKey { get; private set; }

        // Whether the derived key should be saved back to this Purpose object by the ICryptoService,
        // e.g. because this Purpose will be used over and over again. We assume that any built-in
        // Purpose object that is passed without any specific purposes is intended for repeated use,
        // hence the ICryptoService will try to cache cryptographic keys as a performance optimization.
        // If specific purposes have been specified, they were likely generated at runtime, hence it
        // is not appropriate for the keys to be cached in this instance.
        internal bool SaveDerivedKeys { get; set; }

        // Returns a new Purpose which is the specified Purpose plus the specified SpecificPurpose.
        // Leaves the original Purpose unmodified.
        internal Purpose AppendSpecificPurpose(string specificPurpose) {
            // Append the specified specificPurpose to the existing list
            string[] newSpecificPurposes = new string[SpecificPurposes.Length + 1];
            Array.Copy(SpecificPurposes, newSpecificPurposes, SpecificPurposes.Length);
            newSpecificPurposes[newSpecificPurposes.Length - 1] = specificPurpose;
            return new Purpose(PrimaryPurpose, newSpecificPurposes);
        }

        // Returns a new Purpose which is the specified Purpose plus the specified SpecificPurposes.
        // Leaves the original Purpose unmodified.
        internal Purpose AppendSpecificPurposes(IList<string> specificPurposes) {
            // No specific purposes to add
            if (specificPurposes == null || specificPurposes.Count == 0) {
                return this;
            }

            // Append the specified specificPurposes to the existing list
            string[] newSpecificPurposes = new string[SpecificPurposes.Length + specificPurposes.Count];
            Array.Copy(SpecificPurposes, newSpecificPurposes, SpecificPurposes.Length);
            specificPurposes.CopyTo(newSpecificPurposes, SpecificPurposes.Length);
            return new Purpose(PrimaryPurpose, newSpecificPurposes);
        }

        public CryptographicKey GetDerivedEncryptionKey(IMasterKeyProvider masterKeyProvider, KeyDerivationFunction keyDerivationFunction) {
            // has a key already been stored?
            CryptographicKey actualDerivedKey = DerivedEncryptionKey;
            if (actualDerivedKey == null) {
                CryptographicKey masterKey = masterKeyProvider.GetEncryptionKey();
                actualDerivedKey = keyDerivationFunction(masterKey, this);

                // only save the key back to storage if this Purpose is configured to do so
                if (SaveDerivedKeys) {
                    DerivedEncryptionKey = actualDerivedKey;
                }
            }

            return actualDerivedKey;
        }

        public CryptographicKey GetDerivedValidationKey(IMasterKeyProvider masterKeyProvider, KeyDerivationFunction keyDerivationFunction) {
            // has a key already been stored?
            CryptographicKey actualDerivedKey = DerivedValidationKey;
            if (actualDerivedKey == null) {
                CryptographicKey masterKey = masterKeyProvider.GetValidationKey();
                actualDerivedKey = keyDerivationFunction(masterKey, this);

                // only save the key back to storage if this Purpose is configured to do so
                if (SaveDerivedKeys) {
                    DerivedValidationKey = actualDerivedKey;
                }
            }

            return actualDerivedKey;
        }

        // Returns a label and context suitable for passing into the SP800-108 KDF.
        internal void GetKeyDerivationParameters(out byte[] label, out byte[] context) {
            // The primary purpose can just be used as the label directly, since ASP.NET
            // is always in full control of the primary purpose (it's never user-specified).
            if (_derivedKeyLabel == null) {
                _derivedKeyLabel = CryptoUtil.SecureUTF8Encoding.GetBytes(PrimaryPurpose);
            }

            // The specific purposes (which can contain nonce, identity, etc.) are concatenated
            // together to form the context. The BinaryWriter class prepends each element with
            // a 7-bit encoded length to guarantee uniqueness.
            if (_derivedKeyContext == null) {
                using (MemoryStream stream = new MemoryStream())
                using (BinaryWriter writer = new BinaryWriter(stream, CryptoUtil.SecureUTF8Encoding)) {
                    foreach (string specificPurpose in SpecificPurposes) {
                        writer.Write(specificPurpose);
                    }
                    _derivedKeyContext = stream.ToArray();
                }
            }

            label = _derivedKeyLabel;
            context = _derivedKeyContext;
        }

    }
}