보다 더 생생한 기록

[GO] 포인터 사용 공식 (feat. ecdsa.Verify 함수) 본문

Go

[GO] 포인터 사용 공식 (feat. ecdsa.Verify 함수)

viviviviviid 2023. 9. 22. 19:09

아래는 자체라이브러리 crypto 중 ecdsa.Verify() 함수의 기본 형식이다.

func ecdsa.Verify(pub *ecdsa.PublicKey, hash []byte, r *big.Int, s *big.Int) bool

이걸 사용하기 위해선, privateKey struct내에서 PublicKey를 꺼내야한다.

ecdsa.Verify(&privateKey.PublicKey, hashAsBytes, r, s)

 

GO 언어 초심자인 나는 여기서 궁금증이 생겼다. 

 

왜 굳이 PublicKey, r, s 를 포인터로 받아야 하는가? 
함수의 존재 목적과 돌아가는 방식을 보면, 포인터를 이용해서 수정하는 것도 아니고, 그냥 검증하는 함수일 뿐인데 말이다. 
또한 복사본 자체도 메모리를 많이 차지할 것도 아닌것 같은데 굳이 포인터를?

 

구글링을 해본 결과, 3가지 정도의 이유가 나왔다.

 

1. 성능 

포인터를 사용하면 데이터 복사본을 만들 필요가 없다. 복잡한 구조체나 큰 데이터 구조를 값으로 전달하면, 함수를 호출할 때마다 그 데이터의 복사본이 생성되어야 한다. 이로 인해 추가적인 메모리 사용과 CPU 시간이 소요되는데, 이때 포인터를 사용하면 실제 데이터의 위치를 가리키기 때문에 이러한 부가적인 비용을 피할 수 있다.

C++ 언어로 개발 했을때도 포인터를 사용했었지만, 너무 오래전이라 메모리 뿐 아니라 시간도 감축된다는 것을 망각하고 있었다.
최근 들어 js만 사용하다보니 포인터의 필요성을 잊었나보다.

 

2. 일관성

고언어의 표준 라이브러리에서는 종종 포인터를 사용하여 복잡한 구조체를 함수에 전달한다. 이는 일관성을 유지하고 개발자에게 예상 가능한 API를 제공하는데 도움이 된다.

이 내용 덕분에 privateKey struct를 한번더 보게 되었다.
복잡하다고 볼 수는 없지만, big.Int라는 큰 숫자를 가진 형식을 가지고 있기 때문에, 포인터를 사용한 것으로 보인다. 
type PrivateKey struct {
	PublicKey
	D *big.Int
}

type PublicKey struct {
	elliptic.Curve
	X, Y *big.Int
}
또한 일관성을 유지하기 위함이라는 글을 보고 go언어 공식 패키지에 들어가서 crypto/ecdsa를 찾아보았다.
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error)
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error)
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool
func VerifyASN1(pub *PublicKey, hash, sig []byte) bool
type PrivateKey
    func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error)
    func (k *PrivateKey) ECDH() (*ecdh.PrivateKey, error)
    func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool
    func (priv *PrivateKey) Public() crypto.PublicKey
    func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error)
type PublicKey
    func (k *PublicKey) ECDH() (*ecdh.PublicKey, error)
    func (pub *PublicKey) Equal(x crypto.PublicKey) bool
구조체는 전부 포인터로 받고 있는게 보이고, 마지막 함수는 x를 받고 있는데, 이는 crypto.PrivateKey의 인터페이스 타입이다. 포인터가 아닌 다른것들도 받아야 하기에 이렇게 설정해 둔듯 하다. 

결론 : 일관성 하나는 잘 지키는 듯 하다.

 

3. 데이터 변경

함수 내에서 구조체를 수정해야 할 경우에는 포인터를 사용해야만 원래의 데이터에 영향을 줄 수 있다. 

3번은 위 함수에서 이미 수정할 내용이 없는걸 확인했으니 패스.