@@ -53,66 +53,18 @@ private static async Task<AwsCredentials> GetTemporaryCredentialsAsync(string id
53
53
} ;
54
54
}
55
55
56
- private static async Task < JToken > GenerateDataKey ( AwsCredentials credentials , IThirdwebHttpClient httpClient , DateTime ? dateOverride = null )
56
+ private static async Task < JToken > GenerateDataKey ( AwsCredentials credentials , IThirdwebHttpClient httpClient )
57
57
{
58
- var client = Utils . ReconstructHttpClient ( httpClient ) ;
59
58
var endpoint = $ "https://kms.{ AWS_REGION } .amazonaws.com/";
60
59
61
60
var payloadForGenerateDataKey = new { KeyId = _migrationKeyId , KeySpec = "AES_256" } ;
61
+ var requestBodyString = JsonConvert . SerializeObject ( payloadForGenerateDataKey ) ;
62
62
63
- var content = new StringContent ( JsonConvert . SerializeObject ( payloadForGenerateDataKey ) , Encoding . UTF8 , "application/x-amz-json-1.1" ) ;
63
+ var contentType = "application/x-amz-json-1.1" ;
64
64
65
- client . AddHeader ( "X-Amz-Target" , "TrentService.GenerateDataKey" ) ;
65
+ var extraHeaders = new Dictionary < string , string > { { "X-Amz-Target" , "TrentService.GenerateDataKey" } } ;
66
66
67
- var dateTimeNow = dateOverride ?? DateTime . UtcNow ;
68
- var dateStamp = dateTimeNow . ToString ( "yyyyMMdd" ) ;
69
- var amzDateFormat = "yyyyMMddTHHmmssZ" ;
70
- var amzDate = dateTimeNow . ToString ( amzDateFormat ) ;
71
- var canonicalUri = "/" ;
72
-
73
- var canonicalHeaders = $ "host:kms.{ AWS_REGION } .amazonaws.com\n x-amz-date:{ amzDate } \n ";
74
- var signedHeaders = "host;x-amz-date" ;
75
-
76
- #if NETSTANDARD
77
- using var sha256 = SHA256 . Create ( ) ;
78
- var payloadHash = ToHexString ( sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( await content . ReadAsStringAsync ( ) ) ) ) ;
79
- #else
80
- var payloadHash = ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( await content . ReadAsStringAsync ( ) ) ) ) ;
81
- #endif
82
-
83
- var canonicalRequest = $ "POST\n { canonicalUri } \n \n { canonicalHeaders } \n { signedHeaders } \n { payloadHash } ";
84
-
85
- var algorithm = "AWS4-HMAC-SHA256" ;
86
- var credentialScope = $ "{ dateStamp } /{ AWS_REGION } /kms/aws4_request";
87
-
88
- #if NETSTANDARD
89
- var stringToSign = $ "{ algorithm } \n { amzDate } \n { credentialScope } \n { ToHexString ( sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( canonicalRequest ) ) ) } ";
90
- #else
91
- var stringToSign = $ "{ algorithm } \n { amzDate } \n { credentialScope } \n { ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( canonicalRequest ) ) ) } ";
92
- #endif
93
-
94
- var signingKey = GetSignatureKey ( credentials . SecretAccessKey , dateStamp , AWS_REGION , "kms" ) ;
95
- var signature = ToHexString ( HMACSHA256 ( signingKey , stringToSign ) ) ;
96
-
97
- var authorizationHeader = $ "{ algorithm } Credential={ credentials . AccessKeyId } /{ credentialScope } , SignedHeaders={ signedHeaders } , Signature={ signature } ";
98
-
99
- client . AddHeader ( "x-amz-date" , amzDate ) ;
100
- client . AddHeader ( "Authorization" , authorizationHeader ) ;
101
- client . AddHeader ( "x-amz-security-token" , credentials . SessionToken ) ;
102
-
103
- var response = await client . PostAsync ( endpoint , content ) . ConfigureAwait ( false ) ;
104
- var responseContent = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
105
-
106
- if ( ! response . IsSuccessStatusCode )
107
- {
108
- if ( dateOverride == null && responseContent . Contains ( "InvalidSignatureException" ) )
109
- {
110
- var parsedTime = responseContent . Substring ( responseContent . LastIndexOf ( '(' ) + 1 , amzDate . Length ) ;
111
- return await GenerateDataKey ( credentials , httpClient , DateTime . ParseExact ( parsedTime , amzDateFormat , System . Globalization . CultureInfo . InvariantCulture ) . ToUniversalTime ( ) )
112
- . ConfigureAwait ( false ) ;
113
- }
114
- throw new Exception ( $ "Failed to generate data key: { responseContent } ") ;
115
- }
67
+ var responseContent = await PostAwsRequestWithDateOverride ( credentials , httpClient , AWS_REGION , "kms" , endpoint , "/" , "" , requestBodyString , contentType , extraHeaders ) . ConfigureAwait ( false ) ;
116
68
117
69
var responseObject = JToken . Parse ( responseContent ) ;
118
70
var plaintextKeyBlob = responseObject [ "Plaintext" ] ;
@@ -129,54 +81,131 @@ private static async Task<JToken> GenerateDataKey(AwsCredentials credentials, IT
129
81
private static async Task < MemoryStream > InvokeLambdaWithTemporaryCredentialsAsync ( AwsCredentials credentials , string invokePayload , IThirdwebHttpClient httpClient , string lambdaFunction )
130
82
{
131
83
var endpoint = $ "https://lambda.{ AWS_REGION } .amazonaws.com/2015-03-31/functions/{ lambdaFunction } /invocations";
132
- var requestBody = new StringContent ( invokePayload , Encoding . UTF8 , "application/json" ) ;
84
+ var contentType = "application/json" ;
85
+
86
+ var canonicalUri = $ "/2015-03-31/functions/{ Uri . EscapeDataString ( lambdaFunction ) } /invocations";
87
+ var canonicalQueryString = "" ;
88
+
89
+ var extraHeaders = new Dictionary < string , string > ( ) ;
90
+
91
+ var responseContent = await PostAwsRequestWithDateOverride (
92
+ credentials ,
93
+ httpClient ,
94
+ AWS_REGION ,
95
+ "lambda" ,
96
+ endpoint ,
97
+ canonicalUri ,
98
+ canonicalQueryString ,
99
+ invokePayload ,
100
+ contentType ,
101
+ extraHeaders
102
+ )
103
+ . ConfigureAwait ( false ) ;
104
+
105
+ var memoryStream = new MemoryStream ( Encoding . UTF8 . GetBytes ( responseContent ) ) ;
106
+ return memoryStream ;
107
+ }
133
108
109
+ private static async Task < string > PostAwsRequestWithDateOverride (
110
+ AwsCredentials credentials ,
111
+ IThirdwebHttpClient httpClient ,
112
+ string region ,
113
+ string service ,
114
+ string endpoint ,
115
+ string canonicalUri ,
116
+ string canonicalQueryString ,
117
+ string requestBodyString ,
118
+ string contentType ,
119
+ Dictionary < string , string > extraHeaders ,
120
+ DateTime ? dateOverride = null
121
+ )
122
+ {
134
123
var client = Utils . ReconstructHttpClient ( httpClient ) ;
135
124
136
- var dateTimeNow = DateTime . UtcNow ;
125
+ if ( extraHeaders != null )
126
+ {
127
+ foreach ( var kvp in extraHeaders )
128
+ {
129
+ client . AddHeader ( kvp . Key , kvp . Value ) ;
130
+ }
131
+ }
132
+
133
+ var dateTimeNow = dateOverride ?? DateTime . UtcNow ;
134
+ var amzDateFormat = "yyyyMMddTHHmmssZ" ;
135
+ var amzDate = dateTimeNow . ToString ( amzDateFormat ) ;
137
136
var dateStamp = dateTimeNow . ToString ( "yyyyMMdd" ) ;
138
- var amzDate = dateTimeNow . ToString ( "yyyyMMddTHHmmssZ" ) ;
139
137
140
- var canonicalUri = "/2015-03-31/functions/" + Uri . EscapeDataString ( lambdaFunction ) + "/invocations" ;
141
- var canonicalQueryString = "" ;
142
- var canonicalHeaders = $ "host:lambda.{ AWS_REGION } .amazonaws.com\n x-amz-date:{ amzDate } \n ";
138
+ var canonicalHeaders = $ "host:{ new Uri ( endpoint ) . Host } \n " + $ "x-amz-date:{ amzDate } \n ";
143
139
var signedHeaders = "host;x-amz-date" ;
140
+
144
141
#if NETSTANDARD
145
142
using var sha256 = SHA256 . Create ( ) ;
146
- var payloadHash = ToHexString ( sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( invokePayload ) ) ) ;
143
+ var payloadHash = ToHexString ( sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( requestBodyString ) ) ) ;
147
144
#else
148
- var payloadHash = ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( invokePayload ) ) ) ;
145
+ var payloadHash = ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( requestBodyString ) ) ) ;
149
146
#endif
147
+
150
148
var canonicalRequest = $ "POST\n { canonicalUri } \n { canonicalQueryString } \n { canonicalHeaders } \n { signedHeaders } \n { payloadHash } ";
151
149
152
150
var algorithm = "AWS4-HMAC-SHA256" ;
153
- var credentialScope = $ "{ dateStamp } /{ AWS_REGION } /lambda /aws4_request";
151
+ var credentialScope = $ "{ dateStamp } /{ region } / { service } /aws4_request";
154
152
#if NETSTANDARD
155
153
var stringToSign = $ "{ algorithm } \n { amzDate } \n { credentialScope } \n { ToHexString ( sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( canonicalRequest ) ) ) } ";
156
154
#else
157
155
var stringToSign = $ "{ algorithm } \n { amzDate } \n { credentialScope } \n { ToHexString ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( canonicalRequest ) ) ) } ";
158
156
#endif
159
157
160
- var signingKey = GetSignatureKey ( credentials . SecretAccessKey , dateStamp , AWS_REGION , "lambda" ) ;
158
+ var signingKey = GetSignatureKey ( credentials . SecretAccessKey , dateStamp , region , service ) ;
161
159
var signature = ToHexString ( HMACSHA256 ( signingKey , stringToSign ) ) ;
162
160
163
161
var authorizationHeader = $ "{ algorithm } Credential={ credentials . AccessKeyId } /{ credentialScope } , SignedHeaders={ signedHeaders } , Signature={ signature } ";
164
162
165
163
client . AddHeader ( "x-amz-date" , amzDate ) ;
166
164
client . AddHeader ( "Authorization" , authorizationHeader ) ;
167
- client . AddHeader ( "x-amz-security-token" , credentials . SessionToken ) ;
168
165
169
- var response = await client . PostAsync ( endpoint , requestBody ) . ConfigureAwait ( false ) ;
166
+ if ( ! string . IsNullOrEmpty ( credentials . SessionToken ) )
167
+ {
168
+ client . AddHeader ( "x-amz-security-token" , credentials . SessionToken ) ;
169
+ }
170
+
171
+ var content = new StringContent ( requestBodyString , Encoding . UTF8 , contentType ) ;
170
172
173
+ var response = await client . PostAsync ( endpoint , content ) . ConfigureAwait ( false ) ;
171
174
var responseContent = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
172
175
173
176
if ( ! response . IsSuccessStatusCode )
174
177
{
175
- throw new Exception ( $ "Lambda invocation failed: { responseContent } ") ;
178
+ if ( dateOverride == null && responseContent . Contains ( "Signature expired" ) )
179
+ {
180
+ var idx = responseContent . LastIndexOf ( '(' ) ;
181
+ if ( idx > - 1 )
182
+ {
183
+ var parsedTimeString = responseContent . Substring ( idx + 1 , amzDate . Length ) ;
184
+ var serverTime = DateTime . ParseExact ( parsedTimeString , amzDateFormat , System . Globalization . CultureInfo . InvariantCulture ) . ToUniversalTime ( ) ;
185
+
186
+ Console . WriteLine ( $ "Server time: { serverTime } ") ;
187
+
188
+ return await PostAwsRequestWithDateOverride (
189
+ credentials ,
190
+ httpClient ,
191
+ region ,
192
+ service ,
193
+ endpoint ,
194
+ canonicalUri ,
195
+ canonicalQueryString ,
196
+ requestBodyString ,
197
+ contentType ,
198
+ extraHeaders ,
199
+ serverTime
200
+ )
201
+ . ConfigureAwait ( false ) ;
202
+ }
203
+ }
204
+
205
+ throw new Exception ( $ "AWS request failed: { responseContent } ") ;
176
206
}
177
207
178
- var memoryStream = new MemoryStream ( Encoding . UTF8 . GetBytes ( responseContent ) ) ;
179
- return memoryStream ;
208
+ return responseContent ;
180
209
}
181
210
182
211
private static byte [ ] HMACSHA256 ( byte [ ] key , string data )
0 commit comments