Release 2.0.0

This commit is contained in:
Santiago Lezica
2021-01-29 18:51:08 -03:00
parent 8107c4478b
commit cef49eff22
209 changed files with 70157 additions and 926 deletions

View File

@@ -2,8 +2,9 @@ package emergencykit
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"fmt"
"strconv"
"text/template"
"time"
)
@@ -11,7 +12,9 @@ import (
// Input struct to fill the PDF
type Input struct {
FirstEncryptedKey string
FirstFingerprint string
SecondEncryptedKey string
SecondFingerprint string
}
// Output with the html as string and the verification code
@@ -20,24 +23,57 @@ type Output struct {
VerificationCode string
}
var spanishMonthNames = []string{
"Enero",
"Febrero",
"Marzo",
"Abril",
"Mayo",
"Junio",
"Julio",
"Agosto",
"Septiembre",
"Octubre",
"Noviembre",
"Diciembre",
}
// GenerateHTML returns the translated emergency kit html as a string along with the verification code.
func GenerateHTML(params *Input, lang string) (*Output, error) {
verificationCode := randomCode(6)
verificationCode := generateDeterministicCode(params)
// Render output descriptors:
var descriptors string
if params.hasFingerprints() {
descriptors = GetDescriptorsHTML(&DescriptorsData{
FirstFingerprint: params.FirstFingerprint,
SecondFingerprint: params.SecondFingerprint,
})
}
// Render page body:
content, err := render("EmergencyKitContent", lang, &contentData{
// Externally provided:
FirstEncryptedKey: params.FirstEncryptedKey,
SecondEncryptedKey: params.SecondEncryptedKey,
VerificationCode: verificationCode,
// Careful: do not change these format values. See this doc for more info: https://golang.org/pkg/time/#pkg-constants
CurrentDate: time.Now().Format("2006/01/02"), // Format date to YYYY/MM/DD
// Computed by us:
VerificationCode: verificationCode,
CurrentDate: formatDate(time.Now(), lang),
Descriptors: descriptors,
// Template pieces separated for reuse:
IconHelp: iconHelp,
IconPadlock: iconPadlock,
})
if err != nil {
return nil, fmt.Errorf("failed to render EmergencyKitContent template: %w", err)
}
// Render complete HTML page:
page, err := render("EmergencyKitPage", lang, &pageData{
Css: css,
Logo: logo,
Content: content,
})
if err != nil {
@@ -50,17 +86,40 @@ func GenerateHTML(params *Input, lang string) (*Output, error) {
}, nil
}
func randomCode(length int) string {
result := make([]byte, length)
_, err := rand.Read(result)
if err != nil {
panic(err)
func formatDate(t time.Time, lang string) string {
if lang == "en" {
return t.Format("January 2, 2006")
} else {
// Golang has no i18n facilities, so we do our own formatting.
year, month, day := t.Date()
monthName := spanishMonthNames[month-1]
return fmt.Sprintf("%d de %s, %d", day, monthName, year)
}
charset := "0123456789"
for i := 0; i < length; i++ {
result[i] = charset[int(result[i])%len(charset)]
}
func generateDeterministicCode(params *Input) string {
// NOTE:
// This function creates a stable verification code given the inputs to render the Emergency Kit. For now, the
// implementation relies exclusively on the SecondEncryptedKey, which is the Muun key. This is obviously not ideal,
// since we're both dropping part of the input and introducing the assumption that the Muun key will always be
// rendered second -- but it compensates for a problem with one of our clients that causes the user key serialization
// to be recreated each time the kit is rendered (making this deterministic approach useless).
// Create a deterministic serialization of the input:
inputMaterial := params.SecondEncryptedKey
// Compute a cryptographically secure hash of the material (critical, these are keys):
inputHash := sha256.Sum256([]byte(inputMaterial))
// Extract a verification code from the hash (doesn't matter if we discard bytes):
var code string
for _, b := range inputHash[:6] {
code += strconv.Itoa(int(b) % 10)
}
return string(result)
return code
}
func render(name, language string, data interface{}) (string, error) {
@@ -80,12 +139,18 @@ func getContent(name string, language string) string {
switch name {
case "EmergencyKitPage":
return page
case "EmergencyKitContent":
if language == "es" {
return contentES
}
return contentEN
default:
panic("could not find template with name: " + name)
}
}
func (i *Input) hasFingerprints() bool {
return i.FirstFingerprint != "" && i.SecondFingerprint != ""
}