ECIES-X963-SHA256-AESGCM을 살펴보자.

ECIES  ECDH(키 교환) 후 대칭키 암복호화를 수행하는 것을 의미
X963-SHA256  공유키 유도 방식
AES-GCM  AES 대칭키 암호화. GCM은 MAC 역할
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.x963kdf import X963KDF
import os

# AES-GCM 암호화 함수
def aes_gcm_encrypt(key, plaintext):
    iv = os.urandom(12)  # GCM IV (12바이트)
    cipher = Cipher(algorithms.AES(key), modes.GCM(iv))
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    return iv, ciphertext, encryptor.tag  # (IV, 암호문, 인증 태그)

# AES-GCM 복호화 함수
def aes_gcm_decrypt(key, iv, ciphertext, tag):
    cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag))
    decryptor = cipher.decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()

# ECDH 키 교환 및 KDF (X9.63-KDF with SHA-256)
def derive_shared_key(private_key, peer_public_key):
    shared_secret = private_key.exchange(ec.ECDH(), peer_public_key)
    print(f"shared key : {shared_secret.hex()}")
    kdf = X963KDF(algorithm=SHA256(), length=32, sharedinfo=None)  # 256-bit AES key
    return kdf.derive(shared_secret)

# 🔹 1️⃣ Alice와 Bob의 키 쌍 생성 (SECP256R1 사용)
alice_private_key = ec.generate_private_key(ec.SECP256R1())
bob_private_key = ec.generate_private_key(ec.SECP256R1())

alice_public_key = alice_private_key.public_key()
bob_public_key = bob_private_key.public_key()

# 🔹 2️⃣ Alice가 Bob의 공개 키를 받아 대칭 키 생성
alice_shared_key = derive_shared_key(alice_private_key, bob_public_key)

# 🔹 3️⃣ Bob이 Alice의 공개 키를 받아 같은 대칭 키 생성
bob_shared_key = derive_shared_key(bob_private_key, alice_public_key)

assert alice_shared_key == bob_shared_key  # 키가 동일해야 함

# 🔹 4️⃣ Alice가 메시지를 암호화하여 Bob에게 전송
plaintext = b"Hello"
iv, ciphertext, tag = aes_gcm_encrypt(alice_shared_key, plaintext)

# 🔹 5️⃣ Bob이 받은 데이터를 복호화
decrypted_message = aes_gcm_decrypt(bob_shared_key, iv, ciphertext, tag)

print(f"📩 암호화된 메시지: {ciphertext.hex()}")
print(f"🔓 복호화된 메시지: {decrypted_message.decode()}")

1. alice & bob은 secp256r1 곡선으로 키 쌍 생성

2. alice/bob 개인키와 bob/alice 공개키로 ECDH 수행하여 공유키 유도

3. 공유키를 키 유도(X.963-SHA256)하여 공유 AES 대칭키 유도

4. 공유 AES 대칭 키로 AES-GCM 수행

곡선 알고리즘 사용 사례 특징
secp256r1 ECDSA 범용 (NIST 추천) 속도 느림
secp256k1 ECDSA 블록체인 속도 느림
Ed25519 EdDSA SSH 속도 빠름 / 공개키 : 256바이트 (고정 크기)

# secp256k1 예시

from ecdsa import SECP256k1, SigningKey, VerifyingKey
from ecdsa.util import sigencode_der, sigdecode_der
import hashlib

# Alice (서명자)
def alice_sign(message):
    # Alice의 개인키 생성
    sk = SigningKey.generate(curve=SECP256k1)
    # Alice의 공개키 생성
    vk = sk.get_verifying_key()
    
    # 메시지 해싱
    message_hash = hashlib.sha256(message.encode('utf-8')).digest()
    print(f"Alice가 해싱한 메시지: {message_hash.hex()}")
    
    # 서명 생성
    signature = sk.sign(message_hash, sigencode=sigencode_der)
    print(f"Alice의 서명: {signature.hex()}")
    
    return signature, vk

# Bob (검증자)
def bob_verify(message, signature, vk):
    # 메시지 해싱
    message_hash = hashlib.sha256(message.encode('utf-8')).digest()
    print(f"Bob이 해싱한 메시지: {message_hash.hex()}")
    
    # 서명 검증
    try:
        vk.verify(signature, message_hash, sigdecode=sigdecode_der)
        print("서명 검증 성공!")
        return True
    except:
        print("서명 검증 실패!")
        return False

# Alice가 서명
message = "Alice의 중요한 메시지"
print(f"서명할 메시지: {message}")
signature, alice_vk = alice_sign(message)

# Bob이 서명 검증
bob_verify(message, signature, alice_vk)
서명할 메시지: Alice의 중요한 메시지
Alice가 해싱한 메시지: 2491722cc10f5bd7bc9f79ae476ba2c1055dab43be2fca9dc81e0718a1381642
Alice의 서명: 3044022029160eb2534be33a8311639465202dcdee5a7725fc41cd9f01e1c8d986a66688022003c159499de24335b0c89d4a34cc35b0714b58d935ed615cc131b1ab2278d7ad
Bob이 해싱한 메시지: 2491722cc10f5bd7bc9f79ae476ba2c1055dab43be2fca9dc81e0718a1381642
서명 검증 성공!

서명 구조 (der)

SEQUENCE 태그, 서명 구조의 시작 30
서명 길이 44
 r의 INTEGER 태그 02
r의 길이 20 (0x20)
r 값  29 16 0E B2 53 4B E3 3A 83 11 63 94 65 20 2D CD EE 5A 77 25 FC 41 CD 9F 01 E1 C8 D9 86 A6 66 88
s의 INTEGER 태그 02
s의 길이 20 (0x20)
s 값 03 C1 59 49 9D E2 43 35 B0 C8 9D 4A 34 CC 35 B0 71 4B 58 D9 35 ED 61 5C C1 31 B1 AB 22 78 D7 AD

# secp256r1 예시

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
import hashlib

# Alice (서명자)
def alice_sign(message):
    # Alice의 개인키 생성 (secp256r1 사용)
    sk = ec.generate_private_key(ec.SECP256R1())
    
    # Alice의 공개키 생성
    vk = sk.public_key()
    
    # 메시지 해싱
    message_hash = hashlib.sha256(message.encode('utf-8')).digest()
    print(f"Alice가 해싱한 메시지: {message_hash.hex()}")
    
    # 서명 생성
    signature = sk.sign(message_hash, ec.ECDSA(hashes.SHA256()))
    print(f"Alice의 서명: {signature.hex()}")
    
    return signature, vk

# Bob (검증자)
def bob_verify(message, signature, vk):
    # 메시지 해싱
    message_hash = hashlib.sha256(message.encode('utf-8')).digest()
    print(f"Bob이 해싱한 메시지: {message_hash.hex()}")
    
    # 서명 검증
    try:
        vk.verify(signature, message_hash, ec.ECDSA(hashes.SHA256()))
        print("서명 검증 성공!")
        return True
    except:
        print("서명 검증 실패!")
        return False

# Alice가 서명
message = "Alice의 중요한 메시지"
print(f"서명할 메시지: {message}")
signature, alice_vk = alice_sign(message)

# Bob이 서명 검증
bob_verify(message, signature, alice_vk)
서명할 메시지: Alice의 중요한 메시지
Alice가 해싱한 메시지: 2491722cc10f5bd7bc9f79ae476ba2c1055dab43be2fca9dc81e0718a1381642
Alice의 서명: 304402202797f1bd7f59f0f2a7865655fd2999ca1d1e5158b29d546cb71aa57155ab901502201165c74887c2ad384c9f8cf6a7b4de771c4bd06467bf396422bcee39527b60d4
Bob이 해싱한 메시지: 2491722cc10f5bd7bc9f79ae476ba2c1055dab43be2fca9dc81e0718a1381642
서명 검증 성공!

서명 구조가 secp256k1과 동일

# ed25519 예시

from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import hashes
import hashlib

# Alice (서명자)
def alice_sign(message):
    # Alice의 개인키 생성 (ed25519 사용)
    sk = ed25519.Ed25519PrivateKey.generate()
    
    # Alice의 공개키 생성
    vk = sk.public_key()
    
    # 메시지 해싱
    message_hash = hashlib.sha256(message.encode('utf-8')).digest()
    print(f"Alice가 해싱한 메시지: {message_hash.hex()}")
    
    # 서명 생성
    signature = sk.sign(message_hash)
    print(f"Alice의 서명: {signature.hex()}")
    
    return signature, vk

# Bob (검증자)
def bob_verify(message, signature, vk):
    # 메시지 해싱
    message_hash = hashlib.sha256(message.encode('utf-8')).digest()
    print(f"Bob이 해싱한 메시지: {message_hash.hex()}")
    
    # 서명 검증
    try:
        vk.verify(signature, message_hash)
        print("서명 검증 성공!")
        return True
    except:
        print("서명 검증 실패!")
        return False

# Alice가 서명
message = "Alice의 중요한 메시지"
print(f"서명할 메시지: {message}")
signature, alice_vk = alice_sign(message)

# Bob이 서명 검증
bob_verify(message, signature, alice_vk)
서명할 메시지: Alice의 중요한 메시지
Alice가 해싱한 메시지: 2491722cc10f5bd7bc9f79ae476ba2c1055dab43be2fca9dc81e0718a1381642
Alice의 서명: 1a9e8142f0ca566e3bdb67330816c7592c2882d3c3ccd154d5c29c7795aabdc82271aa22370d64adcc1eedb868f9448d25fdbf407b145dda90ba08d8cf29fa00
Bob이 해싱한 메시지: 2491722cc10f5bd7bc9f79ae476ba2c1055dab43be2fca9dc81e0718a1381642
서명 검증 성공!

서명 구조

r (32바이트) 1a9e8142f0ca566e3bdb67330816c7592c2882d3c3ccd154d5c29c7795aabdc8
s (32바이트) 2271aa22370d64adcc1eedb868f9448d25fdbf407b145dda90ba08d8cf29fa00

https://github.com/codetronik/optee-aes

1. 키 설정

TA에서는 OP-TEE API를 활용하여 eMMC의 RPMB(Replay Protected Memory Block) 파티션에 키를 생성하고 삭제할 수 있다. 이 영역은 Normal World에서 접근할 수 없다.

별칭(alias)을 지정하여 키에 접근한다. (TA간 키는 공유할 수 없다. 즉, alias가 동일하더라도, 다른 TA의 alias에 접근할 수 없다.) RPMB에 키가 존재하지 않으면, 키를 생성하여 저장한다. 

TA 관련 주요 API들은 중요 데이터 저장을 위해 세션을 제공한다. 뭘 저장할 지는 자유이며, 필자는 이 곳에 키를 저장하였다.

2. AES-GCM을 구현

세션에서 키를 불러온 후, 이를 사용하여 데이터를 암/복호화한다. TA에서는 암호화 과정에서 IV를 랜덤하게 생성하며, 최종적으로 생성된 태그(tag)와 암호문(cipher)을 함께 반환한다.

+ Recent posts