|
1 | 1 | /************************************************************************************************
|
2 |
| - Random Password Generation |
3 |
| - 1. Defining keyword arguments control the length and characters |
4 |
| - used in the password. |
5 |
| - 2. Building a macro variable containing all allowed characters. |
6 |
| - 3. In a loop, using %SUBSTR(%SYSFUNC(RAND)) to pluck random characters... |
7 |
| - 4. ...and appending them to the growing password. |
8 |
| - 5. Testing our macro via a simple loop and various settings. |
9 |
| - 6. Using a random password for an encryption key on a tempoarary dataset. |
10 |
| -
|
| 2 | + RANDOM PASSWORD GENERATION |
| 3 | + A macro program that generates a random password based on a list of allowed characters. |
11 | 4 | Keywords: Macro
|
12 | 5 | SAS Versions: SAS 9, SAS Viya
|
13 | 6 | Documentation: https://go.documentation.sas.com/doc/en/pgmsascdc/default/mcrolref/titlepage.htm
|
14 |
| -
|
15 |
| - NOTES: |
16 |
| - 1. Only tested with SAS OnDemand for Academics, as that is the only |
17 |
| - SAS version I have access to. |
18 |
| - 2. Does not (currently) support unmatched single or double quote characters |
19 |
| - in the special characterw string. I have not tried mismatched left or |
20 |
| - right parentheses, either. |
| 7 | + 1. Define keyword arguments control the length and characters used in the password. |
| 8 | + 2. Build a macro variable containing all allowed characters. |
| 9 | + 3. In a loop, use %SUBSTR(%SYSFUNC(RAND)) to select random characters... |
| 10 | + 4. ...and append them to the growing password. |
| 11 | + 5. Test macro via a simple loop and various settings. |
| 12 | + 6. Use a random password for an encryption key on a tempoarary dataset. |
| 13 | + NOTE: Does not (currently) support unmatched single or double quote characters |
| 14 | + in the special characterw string. Mismatched left or right parentheses have not been |
| 15 | + tested either. |
21 | 16 | ************************************************************************************************/
|
22 | 17 |
|
23 | 18 | /************************************************************************************************
|
24 |
| - 1. Defining keyword arguments control the length and characters |
| 19 | + 1. Define keyword arguments control the length and characters |
25 | 20 | used in the password.
|
26 | 21 | a. len= is the desired length of the password, defaulting to 24
|
27 | 22 | b. digits= is a Boolean flag, defaulting to TRUE. When TRUE (non-zero),
|
28 | 23 | digits 0-9 are available for use in the password.
|
29 | 24 | c. special= is a set of special characters to also use in passwords. Defaults
|
30 | 25 | to those shown in the %NRSTR(). Use special= to not have any specials.
|
31 | 26 | ************************************************************************************************/
|
| 27 | + |
32 | 28 | %MACRO RANDPASS(len=24,digits=1,special=%NRSTR(@%&#!?.-_+*,/;:));
|
33 | 29 | %LOCAL pass;
|
34 | 30 | %LOCAL chars;
|
|
37 | 33 | %LOCAL i;
|
38 | 34 |
|
39 | 35 | /************************************************************************************************
|
40 |
| - 2. Building a macro variable containing all allowed characters. |
| 36 | + 2. Build a macro variable containing all allowed characters. |
41 | 37 | a. Add the lowercase letters.
|
42 | 38 | b. Add uppercase characters via %UPCASE().
|
43 | 39 | c. Add digits, if requested.
|
44 | 40 | d. Add special characters. Note the use of %SUPERQ() to avoid issues
|
45 | 41 | with &s and %s that may be present in the string.
|
46 | 42 | ************************************************************************************************/
|
| 43 | + |
47 | 44 | %LET chars=abcdefghijklmnopqrstuvwxyz;
|
48 | 45 | %LET chars=&chars%UPCASE(&chars);
|
49 | 46 | %IF &digits %THEN
|
|
53 | 50 | %LET numchars=%length(&chars);
|
54 | 51 |
|
55 | 52 | %DO i=1 %TO &len;
|
56 |
| - /************************************************************************************************ |
57 |
| - 3. In a loop, using %SUBSTR(%SYSFUNC(RAND)) to pluck random characters... |
58 |
| - a. RAND('INTEGER', n) produces a random intger between 1 and n, inclusive |
59 |
| - which is exactly what we need. |
60 |
| - b. %SYSFUNC() invokes it from Macro. |
61 |
| - c. %SUBSTR(string, N, 1) plucks the Nth character from the string. |
62 |
| - ************************************************************************************************/ |
63 |
| - %LET pick=%SUBSTR(&chars,%SYSFUNC(RAND(INTEGER,&numchars)),1); |
64 |
| - |
65 |
| - /************************************************************************************************ |
66 |
| - 4. ...and appending them to the growing password. |
67 |
| - a. Note in particular the shenanigans here. |
68 |
| - b. Simply coding "%LET pass=%SUPERQ(pass)&pick" _does_not_work_. Hidden |
69 |
| - macro quoting characters end up in &pass, in a nested fashion, and |
70 |
| - this produces nested Macro function WARNINGs when the password length |
71 |
| - grows to about 12 characters, which is unacceptable. |
72 |
| - c. The %QSUBSTR() manages to avoid this problem, and also avoids attempts |
73 |
| - to resolve any &foo that might appear in the growing password. |
74 |
| - d. However, %QSUBSTR() can't work on an empty string, so we have to avoid |
75 |
| - it first time though, hence the %IF. |
76 |
| - ************************************************************************************************/ |
77 |
| - %IF &i = 1 %THEN |
78 |
| - %LET pass=&pick; |
79 |
| - %ELSE |
80 |
| - %LET pass=%QSUBSTR(%SUPERQ(pass),1)&pick; |
| 53 | + |
| 54 | +/************************************************************************************************ |
| 55 | + 3. In a loop, use %SUBSTR(%SYSFUNC(RAND)) to select random characters... |
| 56 | + a. RAND('INTEGER', n) produces a random intger between 1 and n, inclusive |
| 57 | + which is exactly what we need. |
| 58 | + b. %SYSFUNC() invokes it from Macro. |
| 59 | + c. %SUBSTR(string, N, 1) plucks the Nth character from the string. |
| 60 | +************************************************************************************************/ |
| 61 | + |
| 62 | + %LET pick=%SUBSTR(&chars,%SYSFUNC(RAND(INTEGER,&numchars)),1); |
| 63 | + |
| 64 | +/************************************************************************************************ |
| 65 | + 4. ...and append them to the growing password. |
| 66 | + a. Note in particular the shenanigans here. |
| 67 | + b. Simply coding "%LET pass=%SUPERQ(pass)&pick" _does_not_work_. Hidden |
| 68 | + macro quoting characters end up in &pass, in a nested fashion, and |
| 69 | + this produces nested Macro function WARNINGs when the password length |
| 70 | + grows to about 12 characters, which is unacceptable. |
| 71 | + c. The %QSUBSTR() manages to avoid this problem, and also avoids attempts |
| 72 | + to resolve any &foo that might appear in the growing password. |
| 73 | + d. However, %QSUBSTR() can't work on an empty string, so we have to avoid |
| 74 | + it first time though, hence the %IF. |
| 75 | +************************************************************************************************/ |
| 76 | + |
| 77 | + %IF &i = 1 %THEN |
| 78 | + %LET pass=&pick; |
| 79 | + %ELSE |
| 80 | + %LET pass=%QSUBSTR(%SUPERQ(pass),1)&pick; |
81 | 81 | %END;
|
82 | 82 |
|
83 | 83 | %* The result of the macro is this generated password;
|
|
86 | 86 | %MEND;
|
87 | 87 |
|
88 | 88 | /************************************************************************************************
|
89 |
| - 5. Testing our macro via a simple loop, and various settings. |
| 89 | + 5. Test macro via a simple loop and various settings. |
90 | 90 | a. We need to define a macro for this. Macro now allows %IF statements in
|
91 | 91 | "open code", but not loops.
|
92 | 92 | b. Just two cases here. All-defaults, plus longer-no-digits-no-specials.
|
93 | 93 | c. The "test" just prints them to the log, for eyeballing.
|
94 | 94 | d. Then we run the loop 3 times to get an idea as to what it's doing.
|
95 | 95 | ************************************************************************************************/
|
| 96 | + |
96 | 97 | %MACRO TEST(n);
|
97 |
| - %DO i=1 %TO &n; |
98 |
| - %PUT pw1=%RANDPASS(); |
99 |
| - %PUT pw2=%RANDPASS(len=30,digits=0,special=); |
| 98 | + %DO i=1 %TO &n; |
| 99 | + %PUT pw1=%RANDPASS(); |
| 100 | + %PUT pw2=%RANDPASS(len=30,digits=0,special=); |
100 | 101 | %END;
|
101 | 102 | %MEND;
|
102 | 103 |
|
103 | 104 | %TEST(3);
|
104 | 105 |
|
105 |
| - |
106 |
| - |
107 | 106 | /************************************************************************************************
|
108 |
| - 6. Using a random password for an encryption key on a tempoarary dataset. |
| 107 | + 6. Use a random password for an encryption key on a tempoarary dataset. |
109 | 108 | a. Generate a longish (60 characters) password, safe from prying eyes.
|
110 |
| - b. We import a list of NSA recruits from a secret location no one has |
| 109 | + b. Import a list of NSA recruits from a secret location no one has |
111 | 110 | ever heard of, and protect it with AES encryption with our generated
|
112 | 111 | password.
|
113 |
| - c. Now we can run analytics on it safely. Here, we run PROC MEANS, and |
| 112 | + c. Now we can run analytics on it safely. Run PROC MEANS, and |
114 | 113 | then ponder why the recruits are all teenagers.
|
115 |
| - d. Now we can drop the table (I used PROC SQL), knowing that even if the |
| 114 | + d. Drop the table (I used PROC SQL), knowing that even if the |
116 | 115 | data was recovered from the disk, the encryption renders it unreadable.
|
117 | 116 | ************************************************************************************************/
|
| 117 | + |
118 | 118 | %LET pw=%randpass(len=60);
|
119 | 119 | %*PUT pw=&pw <== to see it, but its supposed to be a secret;
|
120 | 120 |
|
121 | 121 | DATA nsaclass(ENCRYPT=aes ENCRYPTKEY="&pw");
|
122 |
| - SET sashelp.class; |
| 122 | + SET sashelp.class; |
123 | 123 | RUN;
|
124 | 124 |
|
125 | 125 | PROC MEANS DATA=nsaclass(ENCRYPTKEY="&pw");
|
126 | 126 | RUN;
|
127 | 127 |
|
128 | 128 | PROC SQL;
|
129 |
| - DROP TABLE nsaclass; |
| 129 | + DROP TABLE nsaclass; |
130 | 130 | QUIT;
|
0 commit comments