1 #include <assert.h>
2 #include <time.h>
3 #include <string>
4
5 #include <steamnetworkingsockets/steamnetworkingsockets_certstore.h>
6 #include <google/protobuf/text_format.h>
7 #include <common/crypto.h>
8 #include <common/crypto_25519.h>
9
10 using namespace SteamNetworkingSocketsLib;
11
12 // Time as I write this. So that the tests will still work even after these generated keys expire.
13 time_t k_timeTestNow = 1555374048;
14
GenerateCert(CMsgSteamDatagramCertificateSigned & msgOut,const char * certData,const CECSigningPrivateKey & keyCAPrivateKey,uint64 nCAKeyID)15 void GenerateCert( CMsgSteamDatagramCertificateSigned &msgOut, const char *certData, const CECSigningPrivateKey &keyCAPrivateKey, uint64 nCAKeyID )
16 {
17 msgOut.Clear();
18
19 // Generate a dummy cert with the requested fields and give it a keypair
20 {
21 CMsgSteamDatagramCertificate msgCert;
22 DbgVerify( google::protobuf::TextFormat::ParseFromString( std::string( certData ), &msgCert ) );
23
24 msgCert.set_time_expiry( k_timeTestNow + 3600*8 );
25
26 CECSigningPrivateKey tempIdentityPrivateKey;
27 CECSigningPublicKey tempIdentityPublicKey;
28 CCrypto::GenerateSigningKeyPair( &tempIdentityPublicKey, &tempIdentityPrivateKey );
29 DbgVerify( tempIdentityPublicKey.GetRawDataAsStdString( msgCert.mutable_key_data() ) );
30 msgCert.set_key_type( CMsgSteamDatagramCertificate_EKeyType_ED25519 );
31
32 DbgVerify( msgCert.SerializeToString( msgOut.mutable_cert() ) );
33 }
34
35 // Sign it
36 CryptoSignature_t sig;
37 keyCAPrivateKey.GenerateSignature( msgOut.cert().c_str(), msgOut.cert().length(), &sig );
38 msgOut.set_ca_key_id( nCAKeyID );
39 msgOut.set_ca_signature( &sig, sizeof(sig) );
40 }
41
main()42 int main()
43 {
44 SteamNetworkingErrMsg errMsg;
45
46 //
47 // Populate our cert store with some certs.
48 // See make_test_certs.py
49 //
50
51 // Dynamic (not-hardcoded) self-signed cert
52 // KeyID . . . .: 8112647883641536425
53 // Public key . : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBkU/enzJscDJp0N1RbYkL0E9wXVO5krNr8rm4JDrNBE
54 CECSigningPrivateKey privkey_dynamic_root;
55 DbgVerify( privkey_dynamic_root.ParsePEM( "-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQy NTUxOQAAACAZFP3p8ybHAyadDdUW2JC9BPcF1TuZKza/K5uCQ6zQRAAAAH8SNFZ4EjRWeAAA AAtzc2gtZWQyNTUxOQAAACAZFP3p8ybHAyadDdUW2JC9BPcF1TuZKza/K5uCQ6zQRAAAAEDq vSVEpg9EZkMej6Fw1EFCuiAnNtMCTKmf8ZRXSwzrXRkU/enzJscDJp0N1RbYkL0E9wXVO5kr Nr8rm4JDrNBE -----END OPENSSH PRIVATE KEY----- ", 375 ) );
56 // CA KeyID . . : 8112647883641536425
57 const uint64 k_key_dynamic_root = 8112647883641536425ull;
58 DbgVerify( CertStore_AddCertFromBase64( "Ii4IARIgGRT96fMmxwMmnQ3VFtiQvQT3BdU7mSs2vyubgkOs0ERFmSm1XE2ZkHdgKak/R3xE6pVwMkBlDV+UgOQHEwEg5GnlKLxK5aqKAWl8J0Eo2pl6+grtk5fitu9U15EXtkHhw1o7q8+sZFvRJw8/zXuohkzVB1AC", errMsg ) );
59
60 // Intermediate cert for app (CSGO), signed by hardcoded key
61 // KeyID . . . .: 1790264268120135407
62 // Public key . : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILumwWENaKq+n5xzAvfLgOOaeLvQqky4LzU0HI0qBnU/
63 CECSigningPrivateKey privkey_csgo;
64 DbgVerify( privkey_csgo.ParsePEM( "-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQy NTUxOQAAACC7psFhDWiqvp+ccwL3y4Djmni70KpMuC81NByNKgZ1PwAAAH8SNFZ4EjRWeAAA AAtzc2gtZWQyNTUxOQAAACC7psFhDWiqvp+ccwL3y4Djmni70KpMuC81NByNKgZ1PwAAAEAs mu57b1o/lDSwUKD4LvIM/kQMwFIbzEbFIoyuyDEf3bumwWENaKq+n5xzAvfLgOOaeLvQqky4 LzU0HI0qBnU/ -----END OPENSSH PRIVATE KEY----- ", 375 ) );
65 // CA KeyID . . : 9417917822780561193
66 // Apps . . . . : [730]
67 const uint64 k_key_csgo = 1790264268120135407ull;
68 DbgVerify( CertStore_AddCertFromBase64( "IjEIARIgu6bBYQ1oqr6fnHMC98uA45p4u9CqTLgvNTQcjSoGdT9FmSm1XE2ZkHdgUNoFKSnXp45cKrOCMkCPs0eTzHWsN0oDNrxnAvvi3MiDv6Tv4CudquT4D/nss3usW6xUPD3YIbbISWxL8YE1HGYVRILCYWDCqxoBOK4M", errMsg ) );
69
70 // Cert for particular data center. Not specifically scoped to app, but signed by CSGO cert, so should effectively be scoped.
71 // KeyID . . . .: 10851291850214533835
72 // Public key . : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKd+8wfN8OYAQ+P4fdiC+7xwakeqOlDSqKY5/9wtkUim
73 CECSigningPrivateKey privkey_csgo_eatmwh;
74 DbgVerify( privkey_csgo_eatmwh.ParsePEM( "-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQy NTUxOQAAACCnfvMHzfDmAEPj+H3Ygvu8cGpHqjpQ0qimOf/cLZFIpgAAAH8SNFZ4EjRWeAAA AAtzc2gtZWQyNTUxOQAAACCnfvMHzfDmAEPj+H3Ygvu8cGpHqjpQ0qimOf/cLZFIpgAAAEA0 pWdXwJgrvazaE/69qtE0zsjQJfzshriDJxfC467ktqd+8wfN8OYAQ+P4fdiC+7xwakeqOlDS qKY5/9wtkUim -----END OPENSSH PRIVATE KEY----- ", 375 ) );
75 // CA KeyID . . : 1790264268120135407
76 // POPs . . . . : [u'eat', u'mwh']
77 const uint64 k_key_csgo_eatmwh = 10851291850214533835ull;
78 DbgVerify( CertStore_AddCertFromBase64( "IjgIARIgp37zB83w5gBD4/h92IL7vHBqR6o6UNKopjn/3C2RSKYtdGFlAC1od20ARZkptVxNmZB3YCnvFmDb3UvYGDJAYXfYn+ofbs5Fz4EYiMYNh4SFD302+S/xXsAzmk8awH7nuasCV+RUWjoOshkKMK6ONCYzmkMiD0so7tOR+7zsDQ==", errMsg ) );
79
80 // Intermediate cert for app (TF2), signed by self-signed cert
81 // KeyID . . . .: 12206663272037732248
82 // Public key . : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC/nkdg+La27cA2ptQj1t0buCYoo2OAQI+lf2P/QaRq4
83 CECSigningPrivateKey privkey_tf2;
84 DbgVerify( privkey_tf2.ParsePEM( "-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQy NTUxOQAAACAv55HYPi2tu3ANqbUI9bdG7gmKKNjgECPpX9j/0GkauAAAAH8SNFZ4EjRWeAAA AAtzc2gtZWQyNTUxOQAAACAv55HYPi2tu3ANqbUI9bdG7gmKKNjgECPpX9j/0GkauAAAAEDf 8k3ME+Xapo2rNSUTO7SLog3hNCGP4cWcvM4bnEBkwC/nkdg+La27cA2ptQj1t0buCYoo2OAQ I+lf2P/QaRq4 -----END OPENSSH PRIVATE KEY----- ", 375 ) );
85 // CA KeyID . . : 8112647883641536425
86 // Apps . . . . : [440]
87 const uint64 k_key_tf2 = 12206663272037732248ull;
88 DbgVerify( CertStore_AddCertFromBase64( "IjEIARIgL+eR2D4trbtwDam1CPW3Ru4JiijY4BAj6V/Y/9BpGrhFmSm1XE2ZkHdgULgDKak/R3xE6pVwMkBCdDdDrAn6IkpuRwksFtXHUTgJNtColLLNPdoEhfyg/Fb5EDnTcOmaNzfoJbv2aFGmjPv2CUzYg+G8qKJv09wN", errMsg ) );
89
90 // Another intermediate cert signed by hardcoded root
91 // KeyID . . . .: 15210429824691730624
92 // Public key . : ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO+dnkgm1SI2UAMbGkrotrHeTe30Mu4mhne9s7kb+knI
93 CECSigningPrivateKey privkey_dota_revoked;
94 DbgVerify( privkey_dota_revoked.ParsePEM( "-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQy NTUxOQAAACDvnZ5IJtUiNlADGxpK6Lax3k3t9DLuJoZ3vbO5G/pJyAAAAH8SNFZ4EjRWeAAA AAtzc2gtZWQyNTUxOQAAACDvnZ5IJtUiNlADGxpK6Lax3k3t9DLuJoZ3vbO5G/pJyAAAAEB8 CNRDPePmSmm66c7QyeOSiQyMHtrcouvxqzNq6GnRz++dnkgm1SI2UAMbGkrotrHeTe30Mu4m hne9s7kb+knI -----END OPENSSH PRIVATE KEY----- ", 375 ) );
95 // CA KeyID . . : 9417917822780561193
96 // Apps . . . . : [570]
97 const uint64 k_key_dota_revoked = 15210429824691730624ull;
98 DbgVerify( CertStore_AddCertFromBase64( "IjEIARIg752eSCbVIjZQAxsaSui2sd5N7fQy7iaGd72zuRv6SchFmSm1XE2ZkHdgULoEKSnXp45cKrOCMkCzt988yidn25C8fBC47EyW35w6SA9GbhPx6CUVeI5h8c/GGHrE4d/Mwvm5t3gv37xUg/uSquFhqWuERmUO4xAP", errMsg ) );
99
100
101 // Revoke a key
102 CertStore_AddKeyRevocation( k_key_dota_revoked );
103
104 CMsgSteamDatagramCertificateSigned msgCertSigned;
105 CMsgSteamDatagramCertificate msgCert;
106 const CertAuthScope *pCertScope;
107 const SteamNetworkingPOPID iad = CalculateSteamNetworkingPOPIDFromString( "iad" );
108 const SteamNetworkingPOPID sto = CalculateSteamNetworkingPOPIDFromString( "sto" ); // 7566447
109 const SteamNetworkingPOPID mwh = CalculateSteamNetworkingPOPIDFromString( "mwh" );
110 const SteamNetworkingPOPID eat = CalculateSteamNetworkingPOPIDFromString( "eat" );
111
112 //
113 // Basic check for an identity cert issued by an intermediary.
114 //
115 GenerateCert( msgCertSigned, "app_ids: 730 identity: { generic_string: \"Hercule Poirot\" }", privkey_csgo, k_key_csgo );
116 pCertScope = CertStore_CheckCert( msgCertSigned, msgCert, k_timeTestNow, errMsg );
117 Assert( pCertScope );
118 DbgVerify( CheckCertAppID( msgCert, pCertScope, 730, errMsg ) );
119
120 // Shouldn't work for wrong app
121 DbgVerify( !CheckCertAppID( msgCert, pCertScope, 570, errMsg ) );
122
123 // Should work for any POPID
124 DbgVerify( CheckCertPOPID( msgCert, pCertScope, iad, errMsg ) );
125 DbgVerify( CheckCertPOPID( msgCert, pCertScope, sto, errMsg ) );
126
127 //
128 // Try to use CSGO CA cert to authorize for Dota
129 //
130 GenerateCert( msgCertSigned, "app_ids: 570 identity: { generic_string: \"Hercule Poirot\" }", privkey_csgo, k_key_csgo );
131
132 // Signature should check out here.
133 pCertScope = CertStore_CheckCert( msgCertSigned, msgCert, k_timeTestNow, errMsg );
134 Assert( pCertScope );
135
136 // But app check should fail
137 DbgVerify( !CheckCertAppID( msgCert, pCertScope, 570, errMsg ) );
138
139 //
140 // Cert for data center, signed directly by global app intermediary,
141 // with the POP restriction in the issued cert
142 //
143 Assert( iad == 6906212 );
144 GenerateCert( msgCertSigned, "app_ids: 730 gameserver_datacenter_ids: 6906212", privkey_csgo, k_key_csgo );
145 pCertScope = CertStore_CheckCert( msgCertSigned, msgCert, k_timeTestNow, errMsg );
146 Assert( pCertScope );
147 DbgVerify( CheckCertAppID( msgCert, pCertScope, 730, errMsg ) );
148
149 // Should only work for the authorized POP
150 DbgVerify( CheckCertPOPID( msgCert, pCertScope, iad, errMsg ) );
151 DbgVerify( !CheckCertPOPID( msgCert, pCertScope, sto, errMsg ) );
152
153 //
154 // Cert for data center, signed by app that is further restricted by POPID
155 //
156 Assert( iad == 6906212 );
157 Assert( mwh == 7173992 );
158 GenerateCert( msgCertSigned, "app_ids: 730 gameserver_datacenter_ids: 6906212 gameserver_datacenter_ids: 7173992", privkey_csgo_eatmwh, k_key_csgo_eatmwh );
159 pCertScope = CertStore_CheckCert( msgCertSigned, msgCert, k_timeTestNow, errMsg );
160 Assert( pCertScope );
161 DbgVerify( CheckCertAppID( msgCert, pCertScope, 730, errMsg ) );
162 DbgVerify( !CheckCertPOPID( msgCert, pCertScope, iad, errMsg ) ); // Not in CA chain
163 DbgVerify( CheckCertPOPID( msgCert, pCertScope, mwh, errMsg ) ); // In both CA chain and cert
164 DbgVerify( !CheckCertPOPID( msgCert, pCertScope, eat, errMsg ) ); // In CA chain but not cert
165
166 //
167 // Try to use a cert where only cert is from root that isn't hardcoded
168 //
169 GenerateCert( msgCertSigned, "app_ids: 440 identity: { generic_string: \"Hercule Poirot\" }", privkey_tf2, k_key_tf2 );
170 pCertScope = CertStore_CheckCert( msgCertSigned, msgCert, k_timeTestNow, errMsg );
171 Assert( !pCertScope );
172
173 //
174 // Try to use a cert signed by a revoked key
175 //
176 GenerateCert( msgCertSigned, "app_ids: 570 identity: { generic_string: \"Hercule Poirot\" }", privkey_dota_revoked, k_key_dota_revoked );
177
178 // Should fail
179 pCertScope = CertStore_CheckCert( msgCertSigned, msgCert, k_timeTestNow, errMsg );
180 Assert( !pCertScope );
181
182 return 0;
183 }
184