main_test.go (14457B)
1 package main_test 2 3 import ( 4 "bytes" 5 "crypto/ed25519" 6 "crypto/rand" 7 "crypto/sha512" 8 "encoding/binary" 9 "encoding/json" 10 "fmt" 11 "log" 12 "net/http" 13 "net/http/httptest" 14 "os" 15 "strconv" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/schanzen/taler-go/pkg/merchant" 21 talerutil "github.com/schanzen/taler-go/pkg/util" 22 "taler.net/taler-mailbox/internal/gana" 23 "taler.net/taler-mailbox/internal/util" 24 "taler.net/taler-mailbox/pkg/rest" 25 ) 26 27 var a mailbox.Mailbox 28 29 var testAliceSigningKeyPriv ed25519.PrivateKey 30 var testAliceSigningKey ed25519.PublicKey 31 var testAliceHashedSigningKeyString string 32 33 const merchantConfigResponse = `{ 34 "currency": "KUDOS", 35 "currencies": { 36 "KUDOS": { 37 "name": "Kudos (Taler Demonstrator)", 38 "currency": "KUDOS", 39 "num_fractional_input_digits": 2, 40 "num_fractional_normal_digits": 2, 41 "num_fractional_trailing_zero_digits": 2, 42 "alt_unit_names": { 43 "0": "ク" 44 } 45 } 46 }, 47 "exchanges": [ 48 { 49 "master_pub": "F80MFRG8HVH6R9CQ47KRFQSJP3T6DBJ4K1D9B703RJY3Z39TBMJ0", 50 "currency": "KUDOS", 51 "base_url": "https://exchange.demo.taler.net/" 52 } 53 ], 54 "implementation": "urn:net:taler:specs:taler-merchant:c-reference", 55 "name": "taler-merchant", 56 "version": "18:0:15" 57 }` 58 59 func executeRequest(req *http.Request) *httptest.ResponseRecorder { 60 rr := httptest.NewRecorder() 61 a.Router.ServeHTTP(rr, req) 62 return rr 63 } 64 65 func checkResponseCode(t *testing.T, expected, actual int) bool { 66 if expected != actual { 67 t.Errorf("Expected response code %d, Got %d\n", expected, actual) 68 } 69 return expected == actual 70 } 71 72 var merchServerRespondsPaid = false 73 74 func shouldReturnPaid() bool { 75 return merchServerRespondsPaid 76 } 77 78 func TestMain(m *testing.M) { 79 cfg, err := talerutil.LoadConfiguration("test-mailbox.conf") 80 if err != nil { 81 fmt.Printf("Failed to read config: %v", err) 82 os.Exit(1) 83 } 84 psqlconn := cfg.GetString("mailbox-pq", "connection_string", "postgres:///taler-mailbox") 85 86 db, err := mailbox.OpenDatabase(psqlconn) 87 if err != nil { 88 log.Panic(err) 89 } 90 defer db.Close() 91 merchServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 92 var orderResp merchant.PostOrderRequest 93 if r.URL.Path == "/config" { 94 w.WriteHeader(http.StatusOK) 95 w.Write([]byte(merchantConfigResponse)) 96 return 97 } 98 if !strings.HasPrefix(r.URL.Path, "/private/orders") { 99 fmt.Printf("Expected to request '/private/orders', got: %s\n", r.URL.Path) 100 return 101 } 102 if r.Method == http.MethodPost { 103 err := json.NewDecoder(r.Body).Decode(&orderResp) 104 if err != nil { 105 fmt.Printf("Error %s\n", err) 106 } 107 jsonResp := fmt.Sprintf("{\"order_id\":\"%s\"}", "uniqueOrderId") 108 w.WriteHeader(http.StatusOK) 109 w.Write([]byte(jsonResp)) 110 } else { 111 fmt.Printf("Responding always paid: %v\n", merchServerRespondsPaid) 112 if shouldReturnPaid() { 113 jsonResp := "{\"order_status\":\"paid\"}" 114 w.WriteHeader(http.StatusOK) 115 w.Write([]byte(jsonResp)) 116 } else { 117 jsonResp := "{\"order_status\":\"unpaid\", \"taler_pay_uri\": \"somepaytouri\"}" 118 w.WriteHeader(http.StatusOK) 119 w.Write([]byte(jsonResp)) 120 } 121 } 122 })) 123 defer merchServer.Close() 124 merch := merchant.NewMerchant(merchServer.URL, "supersecret") 125 a.Initialize(mailbox.MailboxConfig{ 126 Version: "testing", 127 DB: db, 128 Merchant: merch, 129 Ini: cfg}) 130 testAliceSigningKey, testAliceSigningKeyPriv, _ = ed25519.GenerateKey(nil) 131 h := sha512.New() 132 h.Write(testAliceSigningKey) 133 testAliceHashedSigningKeyString = util.Base32CrockfordEncode(h.Sum(nil)) 134 135 a.Merchant = merchant.NewMerchant(merchServer.URL, "") 136 137 code := m.Run() 138 // Purge DB 139 a.DB.DeleteAllInboxEntries() 140 os.Exit(code) 141 } 142 143 func TestEmptyMailbox(t *testing.T) { 144 a.DB.DeleteAllInboxEntries() 145 req, _ := http.NewRequest("GET", "/"+testAliceHashedSigningKeyString, nil) 146 response := executeRequest(req) 147 148 checkResponseCode(t, http.StatusNoContent, response.Code) 149 150 body := response.Body.String() 151 if body != "" { 152 t.Errorf("Expected empty response, Got %s", body) 153 } 154 } 155 156 func TestSendMessage(t *testing.T) { 157 testMessage := make([]byte, 256) 158 a.DB.DeleteAllInboxEntries() 159 req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) 160 response := executeRequest(req) 161 162 checkResponseCode(t, http.StatusNoContent, response.Code) 163 164 body := response.Body.String() 165 if body != "" { 166 t.Errorf("Expected empty response, Got %s", body) 167 } 168 } 169 170 func setMailboxPaid(isPaid bool) { 171 var messageFee talerutil.Amount 172 if isPaid { 173 messageFee = talerutil.NewAmount("KUDOS", 1, 0) 174 } else { 175 messageFee = talerutil.NewAmount("KUDOS", 0, 0) 176 } 177 a.MessageFee = &messageFee 178 a.FreeMessageQuota = 1 179 } 180 181 func TestSendMessagePaid(t *testing.T) { 182 183 // Make paid 184 setMailboxPaid(true) 185 186 // Cleanup 187 a.DB.DeleteAllInboxEntries() 188 189 testMessage := make([]byte, 256) 190 rand.Read(testMessage) 191 req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) 192 response := executeRequest(req) 193 194 checkResponseCode(t, http.StatusNoContent, response.Code) 195 196 body := response.Body.String() 197 if body != "" { 198 t.Errorf("Expected empty response, Got %s", body) 199 } 200 testMessage2 := make([]byte, 256) 201 rand.Read(testMessage2) 202 req, _ = http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage2)) 203 response = executeRequest(req) 204 205 checkResponseCode(t, http.StatusPaymentRequired, response.Code) 206 207 body = response.Body.String() 208 if body != "" { 209 t.Errorf("Expected empty response, Got %s", body) 210 } 211 setMailboxPaid(false) 212 } 213 214 func TestGetKeysEmpty(t *testing.T) { 215 req, _ := http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 216 response := executeRequest(req) 217 checkResponseCode(t, http.StatusNotFound, response.Code) 218 } 219 220 func TestMailboxRegistration(t *testing.T) { 221 var msg mailbox.MailboxRegistrationRequest 222 // Dummy pubkey 223 encKey := make([]byte, 32) 224 aliceSigningKey := util.Base32CrockfordEncode(testAliceSigningKey) 225 msg.MailboxMetadata.EncryptionKey = util.Base32CrockfordEncode(encKey) 226 msg.MailboxMetadata.EncryptionKeyType = "X25519" 227 msg.MailboxMetadata.Expiration.Seconds = uint64(time.Now().Add(time.Hour * 24 * 365).Unix()) 228 msg.MailboxMetadata.SigningKey = aliceSigningKey 229 msg.MailboxMetadata.SigningKeyType = "EdDSA" 230 expNbo := make([]byte, 8) 231 binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.Seconds) 232 h := sha512.New() 233 h.Write([]byte(msg.MailboxMetadata.EncryptionKeyType)) 234 h.Write(encKey) 235 h.Write(expNbo) 236 var signedMsg [64 + 4 + 4]byte 237 size := signedMsg[0:4] 238 binary.BigEndian.PutUint32(size, 64+4+4) 239 purp := signedMsg[4:8] 240 binary.BigEndian.PutUint32(purp, gana.TalerSignaturePurposeMailboxRegister) 241 copy(signedMsg[8:], h.Sum(nil)) 242 sig := ed25519.Sign(testAliceSigningKeyPriv, signedMsg[0:]) 243 if !ed25519.Verify(testAliceSigningKey, signedMsg[0:], sig) { 244 t.Errorf("Signature invalid!") 245 } 246 msg.Signature = util.Base32CrockfordEncode(sig) 247 jsonMsg, _ := json.Marshal(msg) 248 req, _ := http.NewRequest("POST", "/register", bytes.NewReader(jsonMsg)) 249 response := executeRequest(req) 250 checkResponseCode(t, http.StatusNoContent, response.Code) 251 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 252 response = executeRequest(req) 253 checkResponseCode(t, http.StatusOK, response.Code) 254 body := response.Body.String() 255 if body == "" { 256 t.Errorf("Expected response, Got %s", body) 257 return 258 } 259 var respMsg mailbox.MailboxMetadata 260 err := json.NewDecoder(response.Body).Decode(&respMsg) 261 fmt.Println(respMsg) 262 if err != nil { 263 t.Errorf("Error %s\n", err) 264 } 265 if respMsg.SigningKey != msg.MailboxMetadata.SigningKey { 266 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 267 } 268 if respMsg.EncryptionKey != msg.MailboxMetadata.EncryptionKey { 269 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 270 } 271 a.DB.DeleteAllMailboxes() 272 a.DB.DeleteAllPendingRegistrations() 273 } 274 275 func createMailboxMetadata(encKey []byte, signKey []byte, info string) mailbox.MailboxMetadata { 276 var mb mailbox.MailboxMetadata 277 aliceSigningKey := util.Base32CrockfordEncode(signKey) 278 mb.EncryptionKey = util.Base32CrockfordEncode(encKey) 279 mb.EncryptionKeyType = "X25519" 280 mb.Expiration.Seconds = uint64(time.Now().Add(time.Hour * 24 * 365).Unix()) 281 mb.SigningKey = aliceSigningKey 282 mb.SigningKeyType = "EdDSA" 283 if info != "" { 284 mb.Info = info 285 } 286 return mb 287 } 288 289 func createMailboxRegistrationMessage(encKey []byte, md mailbox.MailboxMetadata) mailbox.MailboxRegistrationRequest { 290 var msg mailbox.MailboxRegistrationRequest 291 msg.MailboxMetadata = md 292 expNbo := make([]byte, 8) 293 binary.BigEndian.PutUint64(expNbo, msg.MailboxMetadata.Expiration.Seconds) 294 h := sha512.New() 295 h.Write([]byte(msg.MailboxMetadata.EncryptionKeyType)) 296 h.Write(encKey) 297 h.Write(expNbo) 298 var signedMsg [64 + 4 + 4]byte 299 size := signedMsg[0:4] 300 binary.BigEndian.PutUint32(size, 64+4+4) 301 purp := signedMsg[4:8] 302 binary.BigEndian.PutUint32(purp, gana.TalerSignaturePurposeMailboxRegister) 303 copy(signedMsg[8:], h.Sum(nil)) 304 sig := ed25519.Sign(testAliceSigningKeyPriv, signedMsg[0:]) 305 msg.Signature = util.Base32CrockfordEncode(sig) 306 return msg 307 } 308 309 func TestMailboxRegistrationWithInfo(t *testing.T) { 310 var msg mailbox.MailboxRegistrationRequest 311 encKey := make([]byte, 32) 312 md := createMailboxMetadata(encKey, testAliceSigningKey, "") 313 msg = createMailboxRegistrationMessage(encKey, md) 314 jsonMsg, _ := json.Marshal(msg) 315 req, _ := http.NewRequest("POST", "/register", bytes.NewReader(jsonMsg)) 316 response := executeRequest(req) 317 checkResponseCode(t, http.StatusNoContent, response.Code) 318 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 319 response = executeRequest(req) 320 checkResponseCode(t, http.StatusOK, response.Code) 321 body := response.Body.String() 322 if body == "" { 323 t.Errorf("Expected response, Got %s", body) 324 return 325 } 326 var respMsg mailbox.MailboxMetadata 327 err := json.NewDecoder(response.Body).Decode(&respMsg) 328 if err != nil { 329 t.Errorf("Error %s\n", err) 330 } 331 if respMsg.SigningKey != msg.MailboxMetadata.SigningKey { 332 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 333 } 334 if respMsg.EncryptionKey != msg.MailboxMetadata.EncryptionKey { 335 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 336 } 337 if respMsg.Info != "Hello World" { 338 fmt.Printf("Info field missing! %v %v\n", respMsg, msg.MailboxMetadata) 339 } 340 a.DB.DeleteAllMailboxes() 341 a.DB.DeleteAllPendingRegistrations() 342 } 343 344 func TestMailboxRegistrationPaid(t *testing.T) { 345 var msg mailbox.MailboxRegistrationRequest 346 347 // Make paid 348 registrationUpdateFee := talerutil.NewAmount("KUDOS", 1, 0) 349 monthlyFee := talerutil.NewAmount("KUDOS", 2, 0) 350 a.RegistrationUpdateFee = ®istrationUpdateFee 351 a.MonthlyFee = &monthlyFee 352 353 // Dummy pubkey 354 encKey := make([]byte, 32) 355 md := createMailboxMetadata(encKey, testAliceSigningKey, "") 356 msg = createMailboxRegistrationMessage(encKey, md) 357 jsonMsg, _ := json.Marshal(msg) 358 req, _ := http.NewRequest("POST", "/register", bytes.NewReader(jsonMsg)) 359 response := executeRequest(req) 360 checkResponseCode(t, http.StatusPaymentRequired, response.Code) 361 362 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 363 response = executeRequest(req) 364 checkResponseCode(t, http.StatusNotFound, response.Code) 365 366 merchServerRespondsPaid = true 367 req, _ = http.NewRequest("GET", "/info/"+testAliceHashedSigningKeyString, nil) 368 response = executeRequest(req) 369 checkResponseCode(t, http.StatusOK, response.Code) 370 merchServerRespondsPaid = false 371 372 body := response.Body.String() 373 if body == "" { 374 t.Errorf("Expected response, Got %s", body) 375 return 376 } 377 var respMsg mailbox.MailboxMetadata 378 err := json.NewDecoder(response.Body).Decode(&respMsg) 379 if err != nil { 380 fmt.Printf("Error %s\n", err) 381 } 382 if respMsg.SigningKey != msg.MailboxMetadata.SigningKey { 383 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 384 } 385 if respMsg.EncryptionKey != msg.MailboxMetadata.EncryptionKey { 386 fmt.Printf("Keys mismatch! %v %v\n", respMsg, msg.MailboxMetadata) 387 } 388 } 389 390 func TestPostThenDeleteMessage(t *testing.T) { 391 392 // make not paid 393 numMessagesToPost := (a.MessageResponseLimit + 7) 394 testMessages := make([]byte, 256*numMessagesToPost) 395 _, _ = rand.Read(testMessages) 396 a.DB.DeleteAllInboxEntries() 397 398 for i := 0; i < int(numMessagesToPost); i++ { 399 testMessage := testMessages[i*256 : (i+1)*256] 400 req, _ := http.NewRequest("POST", "/"+testAliceHashedSigningKeyString, bytes.NewReader(testMessage)) 401 response := executeRequest(req) 402 403 checkResponseCode(t, http.StatusNoContent, response.Code) 404 405 body := response.Body.String() 406 if body != "" { 407 t.Errorf("Expected empty response, Got %s", body) 408 } 409 } 410 411 req, _ := http.NewRequest("GET", "/"+testAliceHashedSigningKeyString, nil) 412 response := executeRequest(req) 413 414 checkResponseCode(t, http.StatusOK, response.Code) 415 416 if response.Body.Len() != int(256*a.MessageResponseLimit) { 417 t.Errorf("Expected response of 25600 bytes, Got %d", response.Body.Len()) 418 } 419 420 etag := response.Result().Header.Get("ETag") 421 422 if etag == "" { 423 t.Errorf("ETag missing!\n") 424 } 425 426 // Now delete 10 messages 427 h := sha512.New() 428 for i := 0; i < int(a.MessageResponseLimit); i++ { 429 h.Write(testMessages[i*256 : (i+1)*256]) 430 } 431 etagInt, _ := strconv.Atoi(etag) 432 var signedMsg [4 * 4]byte 433 binary.BigEndian.PutUint32(signedMsg[0:4], 4*4) 434 binary.BigEndian.PutUint32(signedMsg[4:8], gana.TalerSignaturePurposeMailboxMessagesDelete) 435 binary.BigEndian.PutUint32(signedMsg[8:12], uint32(etagInt)) 436 binary.BigEndian.PutUint32(signedMsg[12:16], uint32(a.MessageResponseLimit)) 437 sig := ed25519.Sign(testAliceSigningKeyPriv, signedMsg[0:]) 438 if !ed25519.Verify(testAliceSigningKey, signedMsg[0:], sig) { 439 t.Errorf("Signature invalid!") 440 } 441 hAddress := util.Base32CrockfordEncode(testAliceSigningKey) 442 req, _ = http.NewRequest("DELETE", "/"+hAddress+"?count="+strconv.Itoa(int(a.MessageResponseLimit)), nil) 443 req.Header.Add("If-Match", etag) 444 req.Header.Add("Taler-Mailbox-Delete-Signature", util.Base32CrockfordEncode(sig)) 445 response = executeRequest(req) 446 447 checkResponseCode(t, http.StatusNoContent, response.Code) 448 449 body := response.Body.String() 450 if body != "" { 451 t.Errorf("Expected empty response, Got %s", body) 452 } 453 454 req, _ = http.NewRequest("GET", "/"+testAliceHashedSigningKeyString, nil) 455 response = executeRequest(req) 456 457 checkResponseCode(t, http.StatusOK, response.Code) 458 459 if response.Body.Len() != int(256*7) { 460 t.Errorf("Expected response of 256*7 bytes, Got %d", response.Body.Len()) 461 } 462 }