Last active
December 15, 2025 12:55
-
-
Save zlatinz/077f3cb990262686cf05d9e8bd7cb877 to your computer and use it in GitHub Desktop.
ARC300: Password Validator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| using System.Threading.Tasks; | |
| namespace PasswordDemo | |
| { | |
| public static class PasswordValidator | |
| { | |
| // HMAC SHA512 uses a 128 byte key | |
| private const int KEY_LENGTH = 128; | |
| public static bool ValidatePassword(string pwToValidate, string storedHash, string storedSalt) | |
| { | |
| string hashToValidate = EncodePassword(pwToValidate, storedSalt); | |
| return hashToValidate.Equals(storedHash); | |
| } | |
| private static string EncodePassword(string pass, string salt) | |
| { | |
| // Convert provided unicode password string into a byte array | |
| byte[] bIn = Encoding.Unicode.GetBytes(pass); | |
| // Convert the Base64 salt into a byte array | |
| byte[] bSalt = Convert.FromBase64String(salt); | |
| // As HMAC SHA512 is a keyed algorithm, we need a key. | |
| // http://ASP.NET creates this by repeating the salt bytes until they hit the required length | |
| // Essentially this uses the salt as a key, rather than mixing it in with the username. | |
| byte[] bKey = new byte[KEY_LENGTH]; | |
| for (int iter = 0; iter < bKey.Length;) | |
| { | |
| int len = Math.Min(bSalt.Length, bKey.Length - iter); | |
| Buffer.BlockCopy(bSalt, 0, bKey, iter, len); | |
| iter += len; | |
| } | |
| // Create an instance of the hashing algorithm using our key, and hash the provided password bytes | |
| byte[] bRet = null; | |
| using (HMACSHA512 kha = new HMACSHA512(bKey)) | |
| { | |
| bRet = kha.ComputeHash(bIn); | |
| } | |
| // Return the hash bytes as a Base64 string | |
| return Convert.ToBase64String(bRet); | |
| } | |
| static void Main(string[] args) | |
| { | |
| Console.WriteLine(EncodePassword("Guardian02", "DU6oc2em+kFoP0C7GXJQ1g==")); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function PasswordValidator() { | |
| // HMAC SHA512 uses a 128 byte key | |
| const KEY_LENGTH = 128; | |
| this.validatePassword = function (pwToValidate, storedHash, storedSalt) { | |
| var | |
| hashToValidate = this.encodePassword(pwToValidate, storedSalt); | |
| return hashToValidate == storedHash; | |
| } | |
| this.encodePassword = function (pass, salt) { | |
| var | |
| Bytes = require("dw/util/Bytes"), | |
| Encoding = require("dw/crypto/Encoding"), | |
| Mac = require("dw/crypto/Mac"); | |
| // Convert provided unicode password string into a byte array | |
| var bIn = new Bytes(pass, "UTF-16LE"); // Encoding.Unicode.GetBytes(pass) is UTF-16 format using the little endian byte order | |
| // Convert the Base64 salt into a byte array | |
| var bSalt = Encoding.fromBase64(salt); | |
| // As HMAC SHA512 is a keyed algorithm, we need a key. | |
| // http://ASP.NET creates this by repeating the salt bytes until they hit the required length | |
| // Essentially this uses the salt as a key, rather than mixing it in with the username. | |
| var bKeyHex = ""; | |
| var bSaltHex = Encoding.toHex(bSalt); | |
| var iter = 0; | |
| while (iter < KEY_LENGTH*2) { | |
| var len = Math.min(bSaltHex.length, KEY_LENGTH*2 - iter); | |
| bKeyHex += bSaltHex.substr(0, len); | |
| iter += len; | |
| } | |
| var bKey = Encoding.fromHex(bKeyHex); | |
| // Create an instance of the hashing algorithm using our key, and hash the provided password bytes | |
| var bRet = null; | |
| var kha = new Mac(Mac.HMAC_SHA_512); | |
| bRet = kha.digest(bIn, bKey); | |
| // Return the hash bytes as a Base64 string | |
| return Encoding.toBase64(bRet); | |
| } | |
| this.unitTest = function() { | |
| var | |
| Status = require("dw/system/Status"); | |
| var | |
| tests = [ | |
| {password: "Guardian02", salt: "DU6oc2em+kFoP0C7GXJQ1g==", hash: "KCdvGm7EHo8XwE9/QuXh4kDMz32gpUqpDjPc5r4/0OOBlEiK+1zZeUmcvknMWGD6IXeIeR0xWol4JxKn1pscrg=="}, | |
| {password: "Alpha1234", salt: "RU2p9x6eZi+Sj/3yp+4w8A==", hash: "CmRPRFiLcFvhuSeFE6ggNCIS+c21qNVLaKz2imaEXtDmeHXVHOu1FeAERIzMFMFCHBUCC+c+1rMpekqdhL7kZg=="}, | |
| {password: "Beta5678", salt: "vIm6pKU+ZNqJN9aP/b5AQw==", hash: "pxn2RNR/STnurJ9H2soBC2e9ur4OXPJgSR+pWqwtytM+wnFudyWSQ0TW+b2g1Hx3DDP/jN2xr+7MDiNm+r2Wpg=="}, | |
| {password: "Gamma9012", salt: "WcJgQqNfdTHy+sIFnqPy+A==", hash: "i+Txsf+v1XPVcIpBSsexagTVfRJvdcMUedqg6LoJXIkbfoy3Ip2pM3x3+exq1Nz+PeN+NZ3GOmCl+QNXKVM2og=="} | |
| ]; | |
| var passed = 0; | |
| for (var i=0; i<tests.length; i++) { | |
| if ( this.validatePassword(tests[i].password, tests[i].hash, tests[i].salt) ) { | |
| passed++; | |
| } else { | |
| trace("test " + i + " failed"); | |
| } | |
| } | |
| return new Status(passed == tests.length ? Status.OK : Status.ERROR, "Passed " + passed + " from " + tests.length + " tests"); | |
| } | |
| } | |
| var pwValidator = new PasswordValidator(); | |
| return pwValidator.unitTest(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment