Skip to content
Open

done #35

Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 57 additions & 18 deletions Backend/NFC-bridge/NFC-bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,91 @@
from smartcard.util import toHexString
import requests
import json
import threading
import time
import re

# カードの状態を監視するクラス
class MyObserver(CardObserver):
def __init__(self, stop_event):
self.stop_event = stop_event

def update(self, observable, actions):
(addedcards, removedcards) = actions
for card in addedcards:
print(f"+ カードを検出しました")
# 接続してUID(識別番号)を取得するコマンドを送信
# [0xFF, 0xCA, 0x00, 0x00, 0x00] は標準的なUID取得コマンドです
print(f"\n+ カードを検出しました")
connection = card.createConnection()
connection.connect()

# 1. UIDの取得
GET_UID = [0xFF, 0xCA, 0x00, 0x00, 0x00]
data, sw1, sw2 = connection.transmit(GET_UID)
uid = toHexString(data)
print(f" UID: {uid}")

# UIDをサーバーに送信する
url = "http://localhost:3000/NFC" # サーバーのURLを指定
payload ={"uid": uid, "status": "detected"}
# 2. NFC内の暗号化データを読み取る
encrypted_payload = None
try:
raw_bytes = []
# 修正: 16バイト(0x10)ずつ読み取るように戻し、4ブロック(ページ)飛ばしでループ
for block in range(4, 32, 4):
READ_CMD = [0xFF, 0xB0, 0x00, block, 0x10]
read_data, r_sw1, r_sw2 = connection.transmit(READ_CMD)

if r_sw1 == 0x90 and r_sw2 == 0x00:
raw_bytes.extend(read_data)
else:
# ★ここが重要!読み取り拒否された場合のエラーコードを出力
print(f" [Error] ブロック {block} の読み取り失敗: SW1={hex(r_sw1)}, SW2={hex(r_sw2)}")

read_text = bytes(raw_bytes).decode('ascii', errors='ignore')
print(f" [Debug] 読み取った生テキスト: {read_text}")

match = re.search(r'([0-9a-f]{24}):([0-9a-f]{8}):([0-9a-f]{32})', read_text)

encrypted_payload = None
if match:
encrypted_payload = match.group(0)
print(f" ★パスワードデータ検出: {encrypted_payload}")
else:
print(" 指定フォーマットのパスワードデータが見つかりませんでした。")

except Exception as e:
print(f" データ読み取り中にエラーが発生しました: {e}")

# 3. UIDと「文字列のパスワード」をサーバーに送信
url = "http://localhost:5000/NFC"
payload = {
"uid": uid,
"status": "detected",
"encrypted_password": encrypted_payload # 文字列("iv:data:tag")をそのまま送る
}
print(f" [Debug] 送信するJSONデータ: {payload}")

try:
response = requests.post(url, json=payload)

if response.status_code == 200:
print("サーバーに送信しました")
print(" サーバーに送信しました")
self.stop_event.set()
else:
print(f"サーバーへの送信に失敗しました: {response.status_code}")
print(f" サーバーへの送信に失敗しました: {response.status_code}")
except requests.RequestException as e:
print(f"通信エラー: {e}")
print(f" 通信エラー: {e}")

for card in removedcards:
print("- カードが離されました")

# 監視の開始
if __name__ == "__main__":
print("カードリーダーを待機中...")
monitor = CardMonitor()
observer = MyObserver()
stop_event = threading.Event()
observer = MyObserver(stop_event)
monitor.addObserver(observer)

try:
# プログラムを実行し続ける
import time
while True:
while not stop_event.is_set():
time.sleep(1)
except KeyboardInterrupt:
# Ctrl+Cで停止
monitor.removeObserver(observer)
pass
finally:
monitor.deleteObserver(observer)
print("終了します")
31 changes: 31 additions & 0 deletions Backend/Workspace/Routes/CreateRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import VCM from '../Tools/VCM.js';
import DBPerf from '../Tools/DBPerf.js';
import { decrypt } from '../Tools/AESControl.js';
import { CreateMosaicTx } from '../Tools/CreateMosaicTx.js';
import { CreateSupplyTx } from '../Tools/SupplyMosaic.js';
import SignAndAnnounce from '../Tools/SignAndAnnounce.js';

// 注意2: ESMでは __dirname がデフォルトで存在しないため、自作する必要があります
const __filename = fileURLToPath(import.meta.url);
Expand Down Expand Up @@ -76,13 +78,42 @@ router.post("/", VCM('LOGIN_TOKEN', process.env.LOGIN_SECRET), upload.fields([{
const encryptedPrivateKeyObj = JSON.parse(OwnerInfor[0].PrivateKey);
const privateKey = decrypt(inputPassword + process.env.PEPPER, encryptedPrivateKeyObj);

// ===== Mosaic定義トランザクション作成 =====
const { mosaicId, mosaicDefinitionTx, keyPair, facade } = CreateMosaicTx({
networkType: 'testnet',
senderPrivateKey: privateKey,
transferable: false,
deadlineHours: 24
});

// ===== DBに保存する前に、モザイクをブロックチェーンに登録 =====
try {
console.log("[CreateRoom] Announcing Mosaic Definition Transaction...");
await SignAndAnnounce(mosaicDefinitionTx, privateKey, facade, 'https://sym-test-01.opening-line.jp:3001');
console.log("[CreateRoom] Mosaic Definition TX Announced Successfully!");

// ===== 供給量設定トランザクション作成・送信 =====
console.log("[CreateRoom] Creating Supply Change Transaction...");
const { supplyTx, keyPair: supplyKeyPair, facade: supplyFacade } = CreateSupplyTx({
networkType: 'testnet',
senderPrivateKey: privateKey,
mosaicId: mosaicId,
supply: 1_000_000n, // 初期供給量:100万(divisibility=0なので実際の量)
deadlineHours: 24
});

console.log("[CreateRoom] Announcing Supply Change Transaction...");
await SignAndAnnounce(supplyTx, privateKey, supplyFacade, 'https://sym-test-01.opening-line.jp:3001');
console.log("[CreateRoom] Supply Change TX Announced Successfully!");

} catch (txErr) {
console.error("[CreateRoom] Blockchain Transaction Error:", txErr);
return res.status(500).json({
message: "Failed to register mosaic on blockchain",
error: txErr.message
});
}

const RoomIconPath = await saveIcon(req.files.RoomIcon[0], "rooms");
console.log("RoomIcon saved at:", RoomIconPath);
const MosaicIconPath = await saveIcon(req.files.MosaicIcon[0], "tokens");
Expand Down
54 changes: 54 additions & 0 deletions Backend/Workspace/Routes/JoinRoom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import express from 'express';
import DBPerf from '../Tools/DBPerf.js';
import VCM from '../Tools/VCM.js';

const router = express.Router();

router.use(express.json());

router.post('/' , VCM('LOGIN_TOKEN', process.env.LOGIN_SECRET), async (req, res) => {
console.log('/JoinRoom-API is running');

try {
const userId = req.auth.userId;
const inputRoomName = req.body?.roomName;
const roomName = typeof inputRoomName === 'string' ? inputRoomName.trim() : '';

if (!roomName) {
return res.status(400).json({ message: 'Bad Request: roomNameが不足しています' });
}

const roomExists = await DBPerf(
'Check Room Exists',
'SELECT RoomName FROM RoomDetails WHERE RoomName = ?',
[roomName]
);

if (roomExists.length === 0) {
return res.status(404).json({ message: 'Not Found: 指定されたルームは存在しません' });
}

const alreadyJoined = await DBPerf(
'Check Already Joined',
'SELECT 1 FROM Rooms WHERE UserID = ? AND RoomName = ?',
[userId, roomName]
);

if (alreadyJoined.length > 0) {
return res.status(409).json({ message: 'Conflict: 既にこのルームに所属しています' });
}

await DBPerf(
'Insert Room Member',
'INSERT INTO Rooms (UserID, RoomName) VALUES (?, ?)',
[userId, roomName]
);

return res.status(201).json({ message: 'ルーム参加が完了しました' });
} catch (err) {
console.error('JoinRoom-API Error:', err);
return res.status(500).json({ message: 'Internal Server Error' });
}
});

export default router;
66 changes: 40 additions & 26 deletions Backend/Workspace/tmp/NFC.js → Backend/Workspace/Routes/NFC.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,89 @@
const express = require('express');
const path = require('path');
const dotenv = require('dotenv').config();
const DBPerf = require('../Tools/DBPerf');
const argon2 = require('argon2');
import express from 'express';
import path from 'path'; // ※現在のコードでは使用されていないようですが残しています
import dotenv from 'dotenv';
import DBPerf from '../Tools/DBPerf.js'; // ESMでは自作モジュールに拡張子(.js)が必須です
import argon2 from 'argon2';

dotenv.config();

const router = express.Router();

let latestcardUid = null; // 最新のカードUIDを保存する変数
let latestcardEncryptedPassword = null; // 最新のカードの暗号化されたパスワードを保存する変数
let latestcardTime = null; // 最新のカード検知時間を保存する変数

// NFCカードの紐づけエンドポイント
router.post('/NFC/Submit', async(req, res) => {
try{
const {userID, password} = req.body
const nfcRead = false; // NFCカードが読み取られたかのフラグ(実際にはNFCStateなどで管理する)
router.post('/Submit', async (req, res) => {
// 0. 処理開始ログ
console.log("/NFC/Submit-API is running!");
try {
const { userID, userId, password } = req.body;
const normalizedUserID = userID ?? userId;
// 【修正】後で true を代入するため、const ではなく let に変更しました
let nfcRead = false;

if(!latestcardTime || Date.now() - latestcardTime > 10000){
if (!latestcardTime || Date.now() - latestcardTime > 10000) {
return res.status(400).send("カードをもう一度かざしてください");
}

// ユーザーIDとパスワードが送られてきているかの確認
if(!userID || !password){
if (!normalizedUserID || !password) {
console.log("ユーザーIDまたはパスワードが送られてきていません!");
return res.status(400).send({ message: 'User ID and password are required' });
}

// カードUIDが送られてきているかの確認
if(!latestcardUid){
if (!latestcardUid) {
console.log("カードのUIDが送られてきていません!");
return res.status(400).send({ message: 'Card UID is required' });
}

// カードが既に紐づけられていないかの確認
const exitUid = await DBPerf("カードが既に紐づけられていないかの確認","SELECT * FROM NFC WHERE UID = ?",[latestcardUid])
if(exitUid.length != 0){
const exitUid = await DBPerf("カードが既に紐づけられていないかの確認", "SELECT * FROM NFC WHERE UID = ?", [latestcardUid]);
if (exitUid.length != 0) {
console.log("このカードは既に紐づけられています!");
return res.status(400).send({ message: 'This card is already linked to an account' });
}

// ユーザーIDが存在するかの確認とパスワードの照合
const userInfor = await DBPerf("存在するアカウントかの確認","SELECT userID, password FROM users WHERE userID = ?",[userID])
const userInfor = await DBPerf("存在するアカウントかの確認", "SELECT UserID, Password FROM Identify WHERE UserID = ?", [normalizedUserID]);

// ユーザーIDが存在しない場合はダミーパスワードと照合して常に失敗させる(セキュリティ対策)
const comparePassword = userInfor.length == 0 ? process.env.DUMMY_PASSWORD : userInfor[0].password;
if (await argon2.verify(comparePassword, password + process.env.PEPPER)){
const comparePassword = userInfor.length == 0 ? process.env.DUMMY_PASSWORD : userInfor[0].Password;

if (await argon2.verify(comparePassword, password + process.env.PEPPER)) {
// アカウントとNFCカードの紐づけ
await DBPerf("アカウントとNFCカードの紐づけ","INSERT INTO NFC (UID, userID) VALUES (?, ?)",[latestcardUid, userID])
console.log(`カードUID: ${latestcardUid} とユーザーID: ${userID} を紐づけました!`);
await DBPerf("アカウントとNFCカードの紐づけ", "INSERT INTO NFC (UID, UserID) VALUES (?, ?)", [latestcardUid, normalizedUserID]);
console.log(`カードUID: ${latestcardUid} とユーザーID: ${normalizedUserID} を紐づけました!`);
latestcardUid = null;
latestcardTime = null;
nfcRead = true;
res.json({nfcRead: nfcRead});
}
else{
res.json({ nfcRead: nfcRead });
} else {
console.log("パスワードが間違っています!");
return res.status(401).send({ message: 'Invalid password' });
}
}catch(err){
} catch (err) {
console.error("NFCカードの紐づけに失敗しました!", err);
return res.status(500).send({ message: 'Failed to link NFC card' });
}
});

// PythonからカードUIDを受け取るエンドポイント
router.post('/NFC', (req, res) => {
router.post('/', (req, res) => {
latestcardUid = req.body.uid; // Pythonから送られてきたUID
latestcardEncryptedPassword = req.body.encrypted_password; // Pythonから送られてきた暗号化されたパスワードオブジェクト
latestcardTime = Date.now(); // カードが検知された時間を保存
console.log(`カードを検知しました! UID: ${latestcardUid}`);
console.log(`暗号化されたパスワードデータ: ${JSON.stringify(latestcardEncryptedPassword)}`);
globalThis.io?.emit('nfc:detected', {
uid: latestcardUid,
encrypted_password: latestcardEncryptedPassword,
at: latestcardTime
});

// Python側に「無事に受け取ったよ」と返事をする
res.status(200).send({ message: 'Success' });
});

module.exports = router;

export default router;
4 changes: 4 additions & 0 deletions Backend/Workspace/Routes/Register.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ router.post(
// パスワード + Pepper
// PepperはDBに保存しないサーバー専用秘密値
const passwordWithPepper = password + pepper;
console.log("パスワードとペッパーを結合した文字列:", passwordWithPepper);

const encryptPassword = encrypt(process.env.PEPPER, password);
console.log("暗号化されたパスワード:", encryptPassword);
Comment on lines +152 to +155

Copilot AI Feb 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new logs output sensitive credential material (password + pepper, and the encrypted password). Secrets should never be written to logs. Remove these logs and log only non-sensitive metadata if needed.

Suggested change
console.log("パスワードとペッパーを結合した文字列:", passwordWithPepper);
const encryptPassword = encrypt(process.env.PEPPER, password);
console.log("暗号化されたパスワード:", encryptPassword);
// セキュリティのため、パスワードやペッパーを含む値はログに出力しない
const encryptPassword = encrypt(process.env.PEPPER, password);
// セキュリティのため、暗号化されたパスワードもログに出力しない

Copilot uses AI. Check for mistakes.


// =====================================================
Expand Down
Loading