151 lines
4.7 KiB
Go
Raw Normal View History

2020-11-09 10:05:29 -03:00
package libwallet
import (
2021-01-29 18:51:08 -03:00
"encoding/json"
"fmt"
2020-11-09 10:05:29 -03:00
"github.com/muun/libwallet/emergencykit"
)
2021-11-12 19:06:13 -03:00
const (
EKVersionNeverExported = -1
// EKVersionOnlyKeys is the encrypted keys to be written down / emailed
EKVersionOnlyKeys = 1
// EKVersionDescriptors is the first PDF including the descriptors
EKVersionDescriptors = 2
// EKVersionMusig add the musig descriptors
EKVersionMusig = 3
ekVersionCurrent = EKVersionMusig
)
2020-11-09 10:05:29 -03:00
// EKInput input struct to fill the PDF
type EKInput struct {
FirstEncryptedKey string
2021-01-29 18:51:08 -03:00
FirstFingerprint string
2020-11-09 10:05:29 -03:00
SecondEncryptedKey string
2021-01-29 18:51:08 -03:00
SecondFingerprint string
2020-11-09 10:05:29 -03:00
}
// EKOutput with the html as string and the verification code
type EKOutput struct {
HTML string
VerificationCode string
2021-01-29 18:51:08 -03:00
Metadata string
2021-11-12 19:06:13 -03:00
Version int
2020-11-09 10:05:29 -03:00
}
2021-01-29 18:51:08 -03:00
// GenerateEmergencyKitHTML returns the translated html as a string along with the verification
// code and the kit metadata, represented in an opaque string.
// After calling this method, clients should use their Chromium/WebKit implementations to render
// the HTML into a PDF (better done there), and then come back to call `AddEmergencyKitMetadata`
// and produce the final PDF (better done here).
2020-11-09 10:05:29 -03:00
func GenerateEmergencyKitHTML(ekParams *EKInput, language string) (*EKOutput, error) {
2021-01-29 18:51:08 -03:00
moduleInput := &emergencykit.Input{
2020-11-09 10:05:29 -03:00
FirstEncryptedKey: ekParams.FirstEncryptedKey,
2021-01-29 18:51:08 -03:00
FirstFingerprint: ekParams.FirstFingerprint,
2020-11-09 10:05:29 -03:00
SecondEncryptedKey: ekParams.SecondEncryptedKey,
2021-01-29 18:51:08 -03:00
SecondFingerprint: ekParams.SecondFingerprint,
2021-11-12 19:06:13 -03:00
Version: ekVersionCurrent,
2021-01-29 18:51:08 -03:00
}
// Create the HTML and the verification code:
htmlWithCode, err := emergencykit.GenerateHTML(moduleInput, language)
if err != nil {
return nil, fmt.Errorf("GenerateEkHtml failed to render: %w", err)
}
// Create and serialize the metadata:
metadata, err := createEmergencyKitMetadata(ekParams)
if err != nil {
return nil, fmt.Errorf("GenerateEkHtml failed to create metadata: %w", err)
}
metadataBytes, err := json.Marshal(&metadata)
if err != nil {
return nil, fmt.Errorf("GenerateEkHtml failed to marshal %s: %w", string(metadataBytes), err)
}
output := &EKOutput{
HTML: htmlWithCode.HTML,
VerificationCode: htmlWithCode.VerificationCode,
Metadata: string(metadataBytes),
2021-11-12 19:06:13 -03:00
Version: moduleInput.Version,
2021-01-29 18:51:08 -03:00
}
return output, nil
}
// AddEmergencyKitMetadata produces a copy of the PDF file at `srcFile` with embedded metadata,
// writing it into `dstFile`. The provided metadata must be the same opaque string produced by
// `GenerateEmergencyKitHTML`.
func AddEmergencyKitMetadata(metadataText string, srcFile string, dstFile string) error {
// Initialize the MetadataWriter:
metadataWriter := &emergencykit.MetadataWriter{
SrcFile: srcFile,
DstFile: dstFile,
}
// Deserialize the metadata:
var metadata emergencykit.Metadata
err := json.Unmarshal([]byte(metadataText), &metadata)
if err != nil {
return fmt.Errorf("AddEkMetadata failed to unmarshal: %w", err)
}
err = metadataWriter.WriteMetadata(&metadata)
if err != nil {
return fmt.Errorf("AddEkMetadata failed to write metadata: %w", err)
}
return nil
}
func createEmergencyKitMetadata(ekParams *EKInput) (*emergencykit.Metadata, error) {
// NOTE:
// This method would be more naturally placed in the `emergencykit` module, but given the current
// project structure (heavily determined by `gomobile` and the need for top-level bindings) and
// the use of `decodeEncryptedPrivateKey` this isn't possible. Instead, we peek through the layer
// boundary to craft the object here.
// Decode both keys, to extract their inner properties:
2021-03-17 15:28:04 -03:00
firstKey, err := DecodeEncryptedPrivateKey(ekParams.FirstEncryptedKey)
2021-01-29 18:51:08 -03:00
if err != nil {
return nil, fmt.Errorf("createEkMetadata failed to decode first key: %w", err)
}
2021-03-17 15:28:04 -03:00
secondKey, err := DecodeEncryptedPrivateKey(ekParams.SecondEncryptedKey)
2020-11-09 10:05:29 -03:00
if err != nil {
2021-01-29 18:51:08 -03:00
return nil, fmt.Errorf("createEkMetadata failed to decode second key: %w", err)
}
// Obtain the list of checksumed output descriptors:
descriptors := emergencykit.GetDescriptors(&emergencykit.DescriptorsData{
FirstFingerprint: ekParams.FirstFingerprint,
SecondFingerprint: ekParams.SecondFingerprint,
})
// Create the keys for the key array:
keys := []*emergencykit.MetadataKey{
createEmergencyKitMetadataKey(firstKey),
createEmergencyKitMetadataKey(secondKey),
}
metadata := &emergencykit.Metadata{
2021-11-12 19:06:13 -03:00
Version: ekVersionCurrent,
BirthdayBlock: secondKey.Birthday,
2021-01-29 18:51:08 -03:00
EncryptedKeys: keys,
OutputDescriptors: descriptors,
}
return metadata, nil
}
2021-03-17 15:28:04 -03:00
func createEmergencyKitMetadataKey(key *EncryptedPrivateKeyInfo) *emergencykit.MetadataKey {
2021-01-29 18:51:08 -03:00
return &emergencykit.MetadataKey{
2021-03-17 15:28:04 -03:00
DhPubKey: key.EphPublicKey,
EncryptedPrivKey: key.CipherText,
Salt: key.Salt,
2020-11-09 10:05:29 -03:00
}
}