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