diff --git a/wallet/file/sr25519.go b/wallet/file/sr25519.go index 4bb27b73..250dd005 100644 --- a/wallet/file/sr25519.go +++ b/wallet/file/sr25519.go @@ -1,14 +1,19 @@ package file import ( + "crypto/hmac" + "crypto/sha512" + "encoding/binary" "fmt" + "github.com/oasisprotocol/oasis-core/go/common/crypto/signature/signers/memory" + "github.com/tyler-smith/go-bip39" + + "github.com/oasisprotocol/curve25519-voi/primitives/sr25519" "github.com/oasisprotocol/oasis-core/go/common/crypto/sakg" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/crypto/slip10" sdkSignature "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature" - "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/sr25519" - "github.com/tyler-smith/go-bip39" + sdkSr25519 "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/sr25519" ) // Sr25519FromMnemonic derives a signer using ADR-8 from given mnemonic. @@ -27,7 +32,7 @@ func Sr25519FromMnemonic(mnemonic string, number uint32) (sdkSignature.Signer, e seed := bip39.NewSeed(mnemonic, "") - signer, chainCode, err := slip10.NewMasterKey(seed) + _, chainCode, skBinary, err := newMasterKey(seed) if err != nil { return nil, fmt.Errorf("sakg: error deriving master key: %w", err) } @@ -38,16 +43,79 @@ func Sr25519FromMnemonic(mnemonic string, number uint32) (sdkSignature.Signer, e return nil, fmt.Errorf("sakg: error creating BIP-0032 path %s: %w", pathStr, err) } + var signer sdkSignature.Signer for _, index := range path { - signer, chainCode, err = slip10.NewChildKey(signer, chainCode, index) + signer, chainCode, _, err = newChildKey(skBinary, chainCode, index) if err != nil { return nil, fmt.Errorf("sakg: error deriving child key: %w", err) } } - sr25519signer, err := sr25519.NewSigner(signer.(signature.UnsafeSigner).UnsafeBytes()) + return signer, nil +} + +func newMasterKey(seed []byte) (sdkSignature.Signer, slip10.ChainCode, []byte, error) { + // Let S be a seed byte sequence of 128 to 512 bits in length. + if sLen := len(seed); sLen < slip10.SeedMinSize || sLen > slip10.SeedMaxSize { + return nil, slip10.ChainCode{}, nil, fmt.Errorf("slip10: invalid seed") + } + + // 1. Calculate I = HMAC-SHA512(Key = Curve, Data = S) + mac := hmac.New(sha512.New, []byte("ed25519 seed")) + _, _ = mac.Write(seed) + I := mac.Sum(nil) + + // 2. Split I into two 32-byte sequences, IL and IR. + // 3. Use parse256(IL) as master secret key, and IR as master chain code. + return splitDigest(I) +} + +func newChildKey(kPar []byte, cPar slip10.ChainCode, index uint32) (sdkSignature.Signer, slip10.ChainCode, []byte, error) { + if len(kPar) < memory.SeedSize { + return nil, slip10.ChainCode{}, nil, fmt.Errorf("slip10: invalid parent key") + } + + // 1. Check whether i >= 2^31 (whether the child is a hardened key). + if index < 1<<31 { + // If not (normal child): + // If curve is ed25519: return failure. + return nil, slip10.ChainCode{}, nil, fmt.Errorf("slip10: non-hardened keys not supported") + } + + // If so (hardened child): + // let I = HMAC-SHA512(Key = cpar, Data = 0x00 || ser256(kpar) || ser32(i)). + // (Note: The 0x00 pads the private key to make it 33 bytes long.) + var b [4]byte + mac := hmac.New(sha512.New, cPar[:]) + _, _ = mac.Write(b[0:1]) // 0x00 + _, _ = mac.Write(kPar[:memory.SeedSize]) // ser256(kPar) + binary.BigEndian.PutUint32(b[:], index) // Note: The spec neglects to define ser32. + _, _ = mac.Write(b[:]) // ser32(i) + I := mac.Sum(nil) + + // 2. Split I into two 32-byte sequences, IL and IR. + // 3. The returned chain code ci is IR. + // 4. If curve is ed25519: The returned child key ki is parse256(IL). + return splitDigest(I) +} + +func splitDigest(digest []byte) (sdkSignature.Signer, slip10.ChainCode, []byte, error) { + IL, IR := digest[:32], digest[32:] + + var chainCode slip10.ChainCode + + msk, err := sr25519.NewMiniSecretKeyFromBytes(IL) + if err != nil { + return nil, chainCode, nil, err + } + sk := msk.ExpandEd25519() + skBinary, err := sk.MarshalBinary() if err != nil { - return nil, err + return nil, chainCode, nil, err } - return sr25519signer, nil + + signer := sdkSr25519.NewSignerFromKeyPair(sk.KeyPair()) + copy(chainCode[:], IR) + + return signer, chainCode, skBinary, nil }