1- // Copyright (c) .NET Foundation. All rights reserved.
1+ // Copyright (c) .NET Foundation. All rights reserved.
22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
44using System ;
@@ -10,7 +10,6 @@ namespace NuGetGallery.Infrastructure.Authentication
1010 public class ApiKeyV4
1111 {
1212 private const int IdPartLengthBytes = 10 ;
13- private const int PasswordPartLengthBytes = 16 ;
1413 private static readonly byte [ ] IdPrefix = Encoding . ASCII . GetBytes ( "v4" ) ;
1514
1615 internal const int IdPartBase32Length = 20 ;
@@ -80,35 +79,30 @@ public bool Verify(string hashedApiKey)
8079 }
8180
8281 // The verification is not case sensitive. This is to maintain the existing behavior that ApiKey authentication is not case-sensitive.
83- return V3Hasher . VerifyHash ( hashedApiKeyPasswordPart . ToUpperInvariant ( ) . FromBase32String ( ) , PasswordPart ) ;
82+ return V3Hasher . VerifyHash ( hashedApiKeyPasswordPart . ToUpper ( ) . FromBase32String ( ) , PasswordPart ) ;
8483 }
8584
8685 private void CreateInternal ( )
8786 {
88- // Create ID. This will be incorporated into the prefix of the final API key.
89- // After formatting, this will be stored as clear text in the DB for lookup.
90- var idPartBytes = new byte [ IdPartLengthBytes ] ;
91- var passwordPartBytes = new byte [ PasswordPartLengthBytes ] ;
87+ // Create Id
88+ var randomBytes = new byte [ IdPartLengthBytes ] ;
9289 using ( var rng = new RNGCryptoServiceProvider ( ) )
9390 {
94- rng . GetNonZeroBytes ( idPartBytes ) ;
95- rng . GetBytes ( passwordPartBytes ) ;
91+ rng . GetNonZeroBytes ( randomBytes ) ;
9692 }
9793
9894 byte [ ] idBytes = new byte [ IdPartLengthBytes + IdPrefix . Length ] ;
9995 Buffer . BlockCopy ( src : IdPrefix , srcOffset : 0 , dst : idBytes , dstOffset : 0 , count : IdPrefix . Length ) ;
100- Buffer . BlockCopy ( src : idPartBytes , srcOffset : 0 , dst : idBytes , dstOffset : IdPrefix . Length , count : idPartBytes . Length ) ;
96+ Buffer . BlockCopy ( src : randomBytes , srcOffset : 0 , dst : idBytes , dstOffset : IdPrefix . Length , count : randomBytes . Length ) ;
10197
102- // Convert to Base32 string. The length of the string is ApiKeyV4.IdPartBase32Length
98+ // Convert to Base32 string. The length of the string is APIKeyV4_IdPartBase64Length
10399 string idString = idBytes . ToBase32String ( ) . RemoveBase32Padding ( ) ;
104100
105- // Create password. This will become the suffix of the API key and hashed before storing in the DB.
106- var passwordString = passwordPartBytes . ToBase32String ( ) . RemoveBase32Padding ( ) ;
101+ // Create password
102+ var passwordString = Guid . NewGuid ( ) . ToByteArray ( ) . ToBase32String ( ) . RemoveBase32Padding ( ) ;
107103 passwordString = Normalize ( passwordString ) ;
108104
109105 // No need to remove padding or normalize here.. it's stored in the DB and doesn't need to be pretty
110- // The hashed password bytes internally contains parameters for PBKDF2 key derivation, such as the salt,
111- // iteration count, and algorithm used, in addition to the hash itself.
112106 var hashedPasswordString = V3Hasher . GenerateHashAsBytes ( passwordString ) . ToBase32String ( ) ;
113107
114108 IdPart = Normalize ( idString ) ;
@@ -129,7 +123,7 @@ private bool TryParseInternal(string plaintextApiKey)
129123 var id = plaintextApiKey . Substring ( 0 , IdPartBase32Length ) ;
130124 var validId = id
131125 . AppendBase32Padding ( )
132- . ToUpperInvariant ( )
126+ . ToUpper ( )
133127 . TryDecodeBase32String ( out var idBytes ) ;
134128
135129 if ( ! validId )
@@ -158,9 +152,7 @@ private bool TryParseInternal(string plaintextApiKey)
158152
159153 private string Normalize ( string input )
160154 {
161- // This does not change the entropy of the input because the input is a base32 string, which is case
162- // insensitive. The Base32 encoder produces an all uppercase string. The resulting API key is all lowercase.
163155 return input . ToLowerInvariant ( ) ;
164156 }
165157 }
166- }
158+ }
0 commit comments