난수 생성기란 무엇인가요?

난수 생성기(RNG)는 난수 또는 의사 난수를 생성하는 시스템입니다. 다음 숫자를 실제로 예측할 수 있는지 여부는 생성기의 종류에 따라 다릅니다. 어떤 생성기는 물리적 과정에 기반을 두고, 어떤 생성기는 알고리즘으로 숫자를 계산하며, 일부 알고리즘은 이전 결과값을 본 사람이라도 다음에 올 값을 예측할 수 없도록 설계되어 있습니다. 무작위처럼 보이는 것과 실제로 예측할 수 없는 것은 서로 다른 특성이며, 이 글의 대부분은 바로 이 차이점에 관한 것입니다.

하드웨어 난수 생성기

하드웨어 난수 생성기(HRNG)는 진성 난수 생성기(TRNG)라고도 불리며, 물리적 과정에서 숫자를 도출합니다. 가장 오래된 방식은 우리 눈으로 직접 관찰할 수 있는 것들입니다. 바로 던져진 동전, 굴린 주사위, 룰렛 휠입니다. 역학 법칙으로 이들의 움직임을 완벽히 기술할 수 있지만, 정교하게 만들어진 휠이라도 회전이 시작될 때의 미세한 차이가 완전히 다른 결과로 이어지므로 실제로는 예측할 수 없습니다.

현대의 하드웨어 생성기는 대신 미시적인 현상을 측정합니다. 전자 회로의 산탄 잡음과 열잡음, 대기 잡음, 양자 효과 등이 여기에 해당합니다. 이러한 현상은 측정 가능한 예측 불가능성을 의미하는 좋은 엔트로피의 원천이지만, 물리적 소스는 본질적으로 완벽하지 않습니다. 편향이 생길 수 있고, 시간이 지나면서 값이 변동될 수 있으며, 고장이 날 수도 있습니다. 따라서 품질을 평가하고 지속적으로 모니터링해야 하며, 필요한 경우 결과값을 추가로 가공해야 합니다. 이러한 절차는 NIST SP 800-90B와 같은 표준에 상세히 기술되어 있습니다. 하드웨어 소스는 보증이 가장 중요한 분야, 무엇보다도 전송 계층 보안(TLS)과 같은 프로토콜의 기반이 되는 암호화 키에 예측 불가능한 초기 난수를 공급하는 암호학 분야에서 주로 사용됩니다.

의사 난수 생성기

물리적 장치 대신 사용하는 대안은 알고리즘입니다. 의사 난수 생성기(PRNG)는 겉보기에는 무작위 같지만 시드(seed)라고 부르는 초기값에 의해 완전히 결정되는 수열을 만들어냅니다. 동일한 알고리즘에 같은 시드를 입력하면 매번 정확히 동일한 수열이 생성됩니다. 이는 결과가 예측 불가능해야 하거나 시드 및 내부 상태를 추측하거나 복원할 수 있는 환경에서는 약점이 되지만, 시뮬레이션이나 테스트를 똑같이 다시 실행해야 하는 재현성이 필요한 곳에서는 큰 강점이 됩니다. 또한 PRNG는 속도가 빠르고 비용이 적게 들며 구현이 간단하여 대부분의 소프트웨어에서 채택하고 있습니다. 널리 알려진 알고리즘으로는 선형 합동 생성기(LCG), xorshift 생성기, 메르센 트위스터 등이 있습니다.

메르센 트위스터

메르센 트위스터는 마츠모토 마코토와 니시무라 타쿠지가 1997년에 발표한 알고리즘으로, 가장 널리 쓰이는 의사 난수 생성기 중 하나이며 수많은 프로그래밍 언어의 기본값으로 채택되어 있습니다. 이 이름은 수열이 반복되기 전까지의 주기에서 유래했는데, 표준 변형인 MT19937의 경우 그 주기가 메르센 소수인 219937 − 1에 달합니다. 대부분의 무작위성 통계 검정을 통과하므로 시뮬레이션 용도로 잘 맞습니다. 하지만 비밀을 유지하도록 설계되지는 않았습니다. 624개의 연속된 32비트 결과값만 확보하면 누구나 내부 상태를 복원하여 다음에 이어질 모든 값을 예측할 수 있으므로, 암호화 키나 비밀번호 등 예측 불가능성이 유지되어야 하는 용도에는 절대 사용해서는 안 됩니다.

암호학적으로 안전한 생성기와 엔트로피

많은 애플리케이션에서는 알고리즘의 빠른 속도와 물리적 소스의 예측 불가능성이라는 두 가지 요소를 동시에 필요로 합니다. 그 해답이 바로 암호학적으로 안전한 의사 난수 생성기(CSPRNG)입니다. 이 역시 PRNG의 일종이지만, 생성된 값의 일부를 보더라도 나머지 값을 예측할 수 있는 실질적인 방법이 없도록 설계되었으며, 실제 엔트로피 소스로부터 시드를 공급받고 주기적으로 시드를 갱신합니다. 물리적 소스에서 시드를 가져온다고 해서 일반적인 PRNG가 안전해지는 것은 아니며, 알고리즘 자체가 이에 맞게 설계되어 있어야 합니다. 열잡음이나 하드웨어 이벤트 타이밍 같은 물리적 소스가 소량의 진성 무작위성을 제공하면, CSPRNG는 이로부터 훨씬 빠르게 긴 값의 수열을 만들어 냅니다. 운영체제가 실행 중인 프로그램에 제공하는 것이 바로 이 조합이며, 오늘날 실제 환경에서 난수 생성기라고 하면 대개 이를 가리킵니다. 암호화 키, 세션 토큰, 비밀번호 등이 모두 이 방식으로 만들어집니다.

브라우저에서의 난수 생성

JavaScript는 웹 페이지에서 난수를 생성할 수 있는 두 가지 내장 방식을 제공하며, 두 방식은 서로 다른 범주에 속합니다. Math.random()은 일반적인 PRNG입니다. 언어 표준은 구체적인 알고리즘을 각 브라우저의 재량에 맡기고 있으며 보안에 대해서는 아무것도 보장하지 않습니다. 따라서 단순한 애니메이션에는 괜찮지만, 누군가 이의를 제기할 수 있는 공정한 추첨에는 적합하지 않습니다. 다른 하나는 Web Crypto API입니다. 이 인터페이스의 crypto.getRandomValues() 메서드는 운영체제의 엔트로피를 시드로 사용하는 CSPRNG를 통해 암호학적으로 강력한 난수를 반환합니다.

당사의 온라인 난수 생성기는 모든 추첨에 Web Crypto API를 사용하며, 숫자는 서버가 아닌 귀하의 브라우저 내에서 직접 생성됩니다. 본 사이트의 다른 도구에서 주사위를 굴리거나 동전을 던지거나 비밀번호를 생성할 때에도 모두 동일한 소스가 사용됩니다.

무작위 비트에서 원하는 범위의 숫자로

암호학적으로 안전한 생성기를 사용하는 것은 공정한 추첨을 위한 절반의 과정일 뿐입니다. 생성기는 가공되지 않은 순수 비트를 제공하므로 프로그램이 이를 원하는 범위의 숫자로 변환해야 하는데, 바로 이 단계에서 편향이 발생할 수 있습니다. 예를 들어 생성기가 0부터 9까지의 값을 동일한 확률로 제공하는데 0부터 5까지의 숫자가 필요하다고 가정해 보겠습니다. 6으로 나눈 나머지를 구하는 방법이 자연스러워 보이지만, 이렇게 하면 0, 1, 2, 3은 각각 두 가지 경우로 나올 수 있는 반면 4와 5는 한 가지 경우로만 나옵니다. 따라서 0부터 3까지의 숫자가 나올 확률은 각각 20%이지만, 4와 5가 나올 확률은 각각 10%에 불과하게 됩니다. 이러한 편향(modulo bias)을 없애는 한 가지 방법은 범위에 맞지 않는 값을 버리고 다시 뽑는 것입니다. 이에 대한 자세한 내용은 난수 생성기 자료에서 확인하실 수 있습니다.

사람들이 놀라워하는 점이 두 가지 더 있습니다. 첫째는 숫자의 반복이 정상이라는 사실입니다. 1부터 10까지의 정수를 독립적으로 추출하고 모든 숫자의 확률이 같을 때, 방금 뽑힌 숫자가 다시 나올 확률은 다른 숫자와 마찬가지로 정확히 10분의 1입니다. 중복 없는 추첨은 다른 종류의 추첨 방식일 뿐, 더 무작위적인 추첨인 것은 아닙니다. 둘째로, 공정한 생성기 하나만으로 전체 과정의 공정성이 완성되지는 않는다는 점입니다. 당사의 이벤트 당첨자 무작위 추첨 방법 가이드에서 설명하듯이, 참가자 명단 관리와 추첨 시도 횟수 역시 그에 못지않게 중요합니다.