package meowlib import ( "encoding/base64" "fmt" "time" "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/helper" ) type KeyPair struct { Public string `json:"public,omitempty"` Private string `json:"private,omitempty"` Generated time.Time `json:"generated,omitempty"` } type KeysArray []KeyPair func NewKeyPair() (*KeyPair, error) { // Return error! var kp KeyPair keys, err := crypto.GenerateKey("name", "mail", "x25519", 0) if err != nil { return nil, fmt.Errorf("key generation failed: %w", err) } pub, err := keys.GetArmoredPublicKey() if err != nil { return nil, fmt.Errorf("gopenpgp: unable to get public key: %w", err) } priv, err := keys.Armor() if err != nil { return nil, fmt.Errorf("failed to armor private key: %w", err) } kp.Public = base64.StdEncoding.EncodeToString([]byte(pub)) kp.Private = base64.StdEncoding.EncodeToString([]byte(priv)) kp.Generated = time.Now() return &kp, nil } func (Kp *KeyPair) GetCryptoKeyObject() (*crypto.Key, error) { priv, err := base64.StdEncoding.DecodeString(Kp.Private) if err != nil { return nil, fmt.Errorf("failed to decode private key: %w", err) } key, err := crypto.NewKeyFromArmored(string(priv)) if err != nil { return nil, fmt.Errorf("Ccreate key from armoured failed: %w", err) } return key, nil } func AsymEncrypt(publicKey string, data []byte) ([]byte, error) { pub, err := base64.StdEncoding.DecodeString(publicKey) if err != nil { return nil, fmt.Errorf("Message encryption b64 failed: %w", err) } ciphertext, err := encryptMessage(string(pub), crypto.NewPlainMessage(data)) if err != nil { return nil, fmt.Errorf("Message encryption failed: %w", err) } return ciphertext.GetBinary(), err } func AsymDecrypt(PrivateKey string, data []byte) ([]byte, error) { priv, err := base64.StdEncoding.DecodeString(PrivateKey) if err != nil { return nil, fmt.Errorf("Message decryption b64 failed: %w", err) } decrypted, err := decryptMessage(string(priv), nil, crypto.NewPGPMessage(data)) if err != nil { return nil, fmt.Errorf("Message decryption failed: %w", err) } return decrypted.GetBinary(), err } func AsymEncryptArmored(PublicKey string, data []byte) ([]byte, error) { pub, err := base64.StdEncoding.DecodeString(PublicKey) if err != nil { return nil, fmt.Errorf("Message encryption b64 failed: %w", err) } armor, err := helper.EncryptBinaryMessageArmored(string(pub), data) if err != nil { return nil, fmt.Errorf("Message encryption failed: %w", err) } return []byte(armor), err } func AsymDecryptArmored(PrivateKey string, data []byte) ([]byte, error) { priv, err := base64.StdEncoding.DecodeString(PrivateKey) if err != nil { return nil, fmt.Errorf("Message decryption b64 failed: %w", err) } decrypted, err := helper.DecryptBinaryMessageArmored(string(priv), nil, string(data)) if err != nil { return nil, fmt.Errorf("Message decryption failed: %w", err) } return []byte(decrypted), err } /* func AsymEncryptAndSign_helpers(PublicEncryptionKey string, PrivateSignatureKey string, data []byte) (*EncryptedMessage, error) { var enc EncryptedMessage pub, err := base64.StdEncoding.DecodeString(PublicEncryptionKey) if err != nil { log.Error().Msg("Message encryption and sign b64 failed") } priv, err := base64.StdEncoding.DecodeString(PrivateSignatureKey) if err != nil { log.Error().Msg("Message encryption and sign b64 failed") } encrypted, signature, err := helper.EncryptSignBinaryDetached(string(pub), string(priv), nil, data) if err != nil { log.Error().Msg("Message encryption and sign failed") } enc.data = []byte(encrypted) enc.signature = []byte(signature) return &enc, err } func AsymDecryptAndCheck_helpers(MyPrivateEncryptionKey string, MyContactPublicKey string, data []byte, Signature []byte) (DecryptedMessage []byte, err error) { priv, err := base64.StdEncoding.DecodeString(MyPrivateEncryptionKey) if err != nil { log.Error().Msg("Message decryption and sign b64 failed") } pub, err := base64.StdEncoding.DecodeString(MyContactPublicKey) if err != nil { log.Error().Msg("Message decryption and sign b64 failed") } DecryptedMessage, err = helper.DecryptVerifyBinaryDetached(string(pub), string(priv), nil, data, string(Signature)) if err != nil { log.Error().Msg("Message decryption and sign failed") } return DecryptedMessage, err } */ func encryptMessage(key string, message *crypto.PlainMessage) (*crypto.PGPMessage, error) { publicKeyRing, err := createPublicKeyRing(key) if err != nil { return nil, err } ciphertext, err := publicKeyRing.Encrypt(message, nil) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to encrypt message: %w", err) } return ciphertext, nil } func decryptMessage(privateKey string, passphrase []byte, ciphertext *crypto.PGPMessage) (*crypto.PlainMessage, error) { privateKeyObj, err := crypto.NewKeyFromArmored(privateKey) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to parse the private key: %w", err) } privateKeyUnlocked, err := privateKeyObj.Unlock(passphrase) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to unlock key: %w", err) } defer privateKeyUnlocked.ClearPrivateParams() privateKeyRing, err := crypto.NewKeyRing(privateKeyUnlocked) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to create the private key ring: %w", err) } message, err := privateKeyRing.Decrypt(ciphertext, nil, 0) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to decrypt message: %w", err) } return message, nil } func createPublicKeyRing(publicKey string) (*crypto.KeyRing, error) { publicKeyObj, err := crypto.NewKeyFromArmored(publicKey) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to parse public key: %w", err) } if publicKeyObj.IsPrivate() { publicKeyObj, err = publicKeyObj.ToPublic() if err != nil { return nil, fmt.Errorf("gopenpgp: unable to extract public key from private key: %w", err) } } publicKeyRing, err := crypto.NewKeyRing(publicKeyObj) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to create new keyring: %w", err) } return publicKeyRing, nil } func AsymEncryptAndSign(PublicEncryptionKey string, PrivateSignatureKey string, data []byte) (*EncryptedMessage, error) { var enc EncryptedMessage pub, err := base64.StdEncoding.DecodeString(PublicEncryptionKey) if err != nil { return nil, fmt.Errorf("Message encryption and sign b64 failed: %w", err) } priv, err := base64.StdEncoding.DecodeString(PrivateSignatureKey) if err != nil { return nil, fmt.Errorf("Message encryption and sign b64 failed: %w", err) } ciphertext, signature, err := encryptAndSignMessage(string(pub), string(priv), crypto.NewPlainMessage(data)) if err != nil { return nil, fmt.Errorf("Message encryption failed: %w", err) } enc.Data = ciphertext.GetBinary() enc.Signature = []byte(signature) return &enc, err } func AsymDecryptAndCheck(MyPrivateEncryptionKey string, MyContactPublicKey string, data []byte, Signature []byte) (DecryptedMessage []byte, err error) { priv, err := base64.StdEncoding.DecodeString(MyPrivateEncryptionKey) if err != nil { return nil, fmt.Errorf("Message decryption and sign b64 failed: %w", err) } pub, err := base64.StdEncoding.DecodeString(MyContactPublicKey) if err != nil { return nil, fmt.Errorf("Message decryption and sign b64 failed: %w", err) } DecryptedMessage, err = decryptAndCheckMessage(string(pub), string(priv), crypto.NewPGPMessage(data), crypto.NewPGPSignature(Signature)) if err != nil { return nil, fmt.Errorf("Message decryption and sign failed: %w", err) } return DecryptedMessage, err } func encryptAndSignMessage(pub string, priv string, message *crypto.PlainMessage) (*crypto.PGPMessage, []byte, error) { var privateKeyObj, unlockedKeyObj *crypto.Key var privateKeyRing *crypto.KeyRing publicKeyRing, err := createPublicKeyRing(pub) if err != nil { return nil, nil, err } if err != nil { return nil, nil, fmt.Errorf("gopenpgp: unable to encrypt message") } if privateKeyObj, err = crypto.NewKeyFromArmored(priv); err != nil { return nil, nil, fmt.Errorf("gopenpgp: unable to parse private key") } if unlockedKeyObj, err = privateKeyObj.Unlock(nil); err != nil { return nil, nil, fmt.Errorf("gopenpgp: unable to unlock key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { return nil, nil, fmt.Errorf("gopenpgp: unable to create private keyring") } ciphertext, err := publicKeyRing.Encrypt(message, nil) if err != nil { return nil, nil, fmt.Errorf("gopenpgp: unable to encrypt message") } signature, err := privateKeyRing.SignDetached(message) if err != nil { return nil, nil, fmt.Errorf("gopenpgp: unable to encrypt message") } return ciphertext, signature.GetBinary(), nil } func decryptAndCheckMessage(pub string, priv string, message *crypto.PGPMessage, signature *crypto.PGPSignature) ([]byte, error) { var privateKeyObj, unlockedKeyObj *crypto.Key var privateKeyRing *crypto.KeyRing publicKeyRing, err := createPublicKeyRing(pub) if err != nil { return nil, err } if err != nil { return nil, fmt.Errorf("gopenpgp: unable to encrypt message") } if privateKeyObj, err = crypto.NewKeyFromArmored(priv); err != nil { return nil, fmt.Errorf("gopenpgp: unable to parse private key") } if unlockedKeyObj, err = privateKeyObj.Unlock(nil); err != nil { return nil, fmt.Errorf("gopenpgp: unable to unlock key") } defer unlockedKeyObj.ClearPrivateParams() if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil { return nil, fmt.Errorf("gopenpgp: unable to create private keyring") } plainmessage, err := privateKeyRing.Decrypt(message, nil, 0) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to decrypt message") } err = publicKeyRing.VerifyDetached(plainmessage, signature, crypto.GetUnixTime()) if err != nil { return nil, fmt.Errorf("gopenpgp: unable to check message signature") } return plainmessage.GetBinary(), nil }