firestore: use crypto/rand to generate new doc IDs

Change-Id: Ifc564eb5a5953bf5740ecd9e8ec0b3a431d99cc5
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/44570
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
diff --git a/firestore/collref.go b/firestore/collref.go
index 77deac8..6dff217 100644
--- a/firestore/collref.go
+++ b/firestore/collref.go
@@ -16,10 +16,9 @@
 
 import (
 	"context"
-	"math/rand"
-	"os"
-	"sync"
-	"time"
+	"crypto/rand"
+	"encoding/base64"
+	"fmt"
 )
 
 // A CollectionRef is a reference to Firestore collection.
@@ -97,6 +96,9 @@
 }
 
 // NewDoc returns a DocumentRef with a uniquely generated ID.
+//
+// NewDoc will panic if crypto/rand cannot generate enough bytes to make a new
+// doc ID.
 func (c *CollectionRef) NewDoc() *DocumentRef {
 	return c.Doc(uniqueID())
 }
@@ -123,19 +125,10 @@
 	return newDocumentRefIterator(ctx, c, nil)
 }
 
-const alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
-
-var (
-	rngMu sync.Mutex
-	rng   = rand.New(rand.NewSource(time.Now().UnixNano() ^ int64(os.Getpid())))
-)
-
 func uniqueID() string {
-	var b [20]byte
-	rngMu.Lock()
-	for i := 0; i < len(b); i++ {
-		b[i] = alphanum[rng.Intn(len(alphanum))]
+	b := make([]byte, 32)
+	if _, err := rand.Read(b); err != nil {
+		panic(fmt.Sprintf("firestore: crypto/rand.Read error: %v", err))
 	}
-	rngMu.Unlock()
-	return string(b[:])
+	return base64.RawURLEncoding.EncodeToString(b)
 }
diff --git a/firestore/collref_test.go b/firestore/collref_test.go
index be45bf8..4624cbb 100644
--- a/firestore/collref_test.go
+++ b/firestore/collref_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"context"
+	"encoding/base64"
 	"testing"
 
 	"github.com/golang/protobuf/proto"
@@ -41,10 +42,14 @@
 	coll := c.Collection("C")
 	got := coll.NewDoc()
 	if got.Parent != coll {
-		t.Errorf("got %v, want %v", got.Parent, coll)
+		t.Errorf("NewDoc got %v, want %v", got.Parent, coll)
 	}
-	if len(got.ID) != 20 {
-		t.Errorf("got %d-char ID, wanted 20", len(got.ID))
+	b, err := base64.RawURLEncoding.DecodeString(got.ID)
+	if err != nil {
+		t.Fatalf("NewDoc DecodeToString(%q) got err: %v", got.ID, err)
+	}
+	if len(b) != 32 {
+		t.Errorf("NewDoc got %d-byte ID, wanted 32", len(b))
 	}
 
 	got2 := coll.NewDoc()