package mfa import ( "encoding/base64" "encoding/hex" "testing" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/pquerna/otp/totp" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) func TestPackUnpackBox(t *testing.T) { unlockerKeys := generateKeys(t, 3) unlockerPublicKeys := make([]*keys.PublicKey, len(unlockerKeys)) for i, key := range unlockerKeys { unlockerPublicKeys[i] = key.PublicKey() } otpKey, err := totp.Generate(totp.GenerateOpts{ Issuer: "iam-" + hex.EncodeToString(unlockerPublicKeys[0].Bytes()), AccountName: "NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM (devenv)", }) require.NoError(t, err) box, err := PackMFABox(otpKey, unlockerPublicKeys) require.NoError(t, err) // make sure secrets are encrypted secrets := new(Secrets) err = proto.Unmarshal(box.GetEncryptedSecrets(), secrets) require.Error(t, err) // make sure MFA secret encryption key is encrypted _, err = decryptData(box.EncryptedSecrets, box.Unlockers[0].EncryptedSecretsKey, box.Salt) require.Error(t, err) for _, unlockerKey := range unlockerKeys { otpKeyFromBox, err := UnpackMFABox(box, unlockerKey) require.NoError(t, err) require.Equal(t, otpKey.URL(), otpKeyFromBox.URL()) } randomKey := generateKeys(t, 1) _, err = UnpackMFABox(box, randomKey[0]) require.Error(t, err) } func TestProprietaryCompatibility(t *testing.T) { for _, tc := range []struct { binary string // base64 encoded MFA Box otpURL string // secret unlockerPrivateKeys []string // hex-encoded private keys }{ { // this is MFA box created with proprietary code with HKDF salt binary: "Cn8KIQNSH3wIalCMX35hjaEV9sYCJYJ9QC1EFy1eTH/ZaTdaIxJIl6m1UcOuwmtkkcwIqLbX+DNiIvzkGw29YiWbyycvWh048nf4phBKXkzIMy7GXzKJz1n3BWV7q9QPezpLkjeU1Nn2u1czyXrnGhCJ7VGazNvBg50zsokWEXXWCn8KIQK/IVlNTlmdqA0+XxuUR5KjRvEOYVmyO9JjUMSGaFb0ERJIlmYTw2HcLvscJCLiLLGBh+lXRaFUOqCYgKwwT5G352cYJnMejeQ6QlIlzygHTn9N+RHvh4Hmy5Pt1SUik6hVvfil/9LsX8T0GhDHh7f4v3mjmSgU4T4mjX7lCn8KIQLnAI+bca1SzSSPtsp9n6k2+uC4pVPw4rY0eYKUu6fJ4hJI5Pb7qVzFMDQcTLxLrA0vIQ4DEnwGt78QMdZaHM4FqosIMO0C6TLoTUOyXSgK/scF785JEH0freFiOh+fR5OqQNCrLHMjzoL4GhA2xmq4WmFuRaToK9pm4B0YEiEDWC0ggRO0nfUoYBDO58amY6Y13Zs4h05gjLXIp23y8r4avwEoZqY9aG/sHlso1pRtMtSUJdFGpsONIQvhG3nnJHEy1vbDiVzmThK9Onn2huX5vGJ4iKZX8sj2pd9/yo+twqKA9osNQDlVnJikd5wapnPWKKxjAN22pIQC3TJV0AXJdtklg39YO9H0cz5r2ZDFTWOlCZdOnBxoDW3CyxUX+lQMA5GJ/v+zVPQpCu8no8z1NdLNbiIHI6Gn6e0hfSENMeizH3QvKLLaAqR7VUK3dHNAhyHquIe05xt28RImSnZewCIQf3et/IfqVLm8QU9kcfGIFA==", otpURL: "otpauth://totp/iam-test:NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM?algorithm=SHA1&digits=6&issuer=iam-test&period=30&secret=2EIZ6JTGWBZHYEMTFQGTCWLUIGHMNU2S", unlockerPrivateKeys: []string{ "b4e6b0cfd844f454a1cd0c726455356aa2120743d112805cfb05a67202b6a7a4", "679f5eeab1a885ae7c8c2992149145265f46458cacc8191f6b6902aa7e7efdc8", "e3ac3501472ab144dfc99d61b69e3fccc6ffa63519a728307624e9aa1e99350d", }, }, { // this is MFA box created with proprietary code without HKDF salt binary: "Cm0KIQPjDLGDyyM2KTMx7JxNGYCcY+8V74wgFehEc10gxPde4RJIGWiM6mxo9q0EF8rp/pdFULNy9O+BQc89NIess3z5nSmWFFZxx7j/yKF22hRxLQYenxciHBC0PhbNGXoHG7H4q1a9rhlftcdbCm0KIQOxT6G7TamFLQnktNXXkPrVEXgO4w6yEJ7SQScsXc30EBJI7PSxnqIZmyufYiYkhmwhtP9OSziIA2mFbSz+Wwp45XLKkLYVsT+1Bvj2fKLPZdb5LIPielfRXZpoCrHo8jSDigFae5zxBfsSCm0KIQPEdTuOmmavHwgjPCCgh2ldCTNpubMNzcUc7zakNTDB9hJIl25dRwdxqdV1secWXnhlX6QGWOy8jIswE3MFP0mHhE5yy9jctpIhQKctOCtdsDdMZ081T72omxEc8wRdOK83/cE54P8G7cjPEiECRnc7bPPWVRZtcd5xc4Vvu4LXak16RwA09x4oKsYMB/MavwELRmMpyMwoFgixJrCdFg7PFML4SEb9LE2sj361psAS4kslwIDWMWMPJr110dtzMIL2wOl9AlfpHqHd6CRDhNIv5zOUTyvLSiFZbhC9l8mjEd672uBMlvkSZALOGTnsIigp31a1nDlbyuo9YuGddYGHVyg0tpN3RtHnklL/Bp3rdVGbjPqdo6stp8993syJ1+8mxAE0SvnN/AkuwMODlAHbtieoENVxZWHTtPX0FuFbv9v69I5Sm30RkuKqruPFlw==", otpURL: "otpauth://totp/iam-test:NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM?algorithm=SHA1&digits=6&issuer=iam-test&period=30&secret=2EIZ6JTGWBZHYEMTFQGTCWLUIGHMNU2S", unlockerPrivateKeys: []string{ "17cbdf19c6ebb8c8e7753c489ee7851bf6fe9d157f5dc382bd55a84547cfe050", "a7b0d0e3e88f2dafa73606d39cadfcf18d1a91d606db70e95815f15b9bd34ea6", "b7f93214f7897a863cd9e0bdc3dfb1d7bc1fa49672d5e930d7682b22e1341076", }, }, } { boxData, err := base64.StdEncoding.DecodeString(tc.binary) require.NoError(t, err) var box = new(MFABox) require.NoError(t, box.Unmarshal(boxData)) for _, keyStr := range tc.unlockerPrivateKeys { key, err := keys.NewPrivateKeyFromHex(keyStr) require.NoError(t, err) otpKey, err := UnpackMFABox(box, key) require.NoError(t, err) require.Equal(t, tc.otpURL, otpKey.URL()) } } } func generateKeys(t *testing.T, n int) []*keys.PrivateKey { var err error res := make([]*keys.PrivateKey, n) for i := 0; i < n; i++ { res[i], err = keys.NewPrivateKey() require.NoError(t, err) } return res }