contract ERC1271Account is IERC1271, BaseAccount {

    // NOTE: Valid magic value bytes4(keccak256("isValidSignature(bytes,bytes)")
    bytes4 constant private VALID_SIG = 0x20c13b0b;
    // NOTE: Invalid magic value
    bytes4 constant private INVALID_SIG = 0xffffffff;

    /**
     *  Public functions
     */

    /// @dev Check if a message and signature pair is valid
    /// @notice The _signatures parameter can either be one auth key signature or it can
    /// @notice be a login key signature and an auth key signature (signed login key)
    /// @param _msg Message that was signed
    /// @param _signatures Signature(s) of the data. Either a single signature (login) or two (login and auth)
    /// @return VALID_SIG or INVALID_SIG hex data
    function isValidSignature(
        bytes memory _msg,
        bytes memory _signatures
    )
        public
        view
        returns (bytes4)
    {
        if (_signatures.length == 65) {
            return isValidAuthKeySignature(_msg, _signatures);
        } else if (_signatures.length == 130) {
            return isValidLoginKeySignature(_msg, _signatures);
        } else {
            revert("Invalid _signatures length");
        }
    }

    /// @dev Check if a message and auth key signature pair is valid
    /// @param _msg Message that was signed
    /// @param _signature Signature of the data signed by the authkey
    /// @return VALID_SIG or INVALID_SIG hex data
    function isValidAuthKeySignature(
        bytes memory _msg,
        bytes memory _signature
    )
        public
        view
        returns (bytes4)
    {
        address authKeyAddress = _getEthSignedMessageHash(_msg).recover(
            _signature
        );

        bytes4 sig;
        _isValidAuthKey(authKeyAddress) ? sig = VALID_SIG : sig = INVALID_SIG;
        return sig;
    }

    /// @dev Check if a message and login key signature pair is valid, as well as a signed login key by an auth key
    /// @param _msg Message that was signed
    /// @param _signatures Signatures of the data. Signed msg data by the login key and signed login key by auth key
    /// @return VALID_SIG or INVALID_SIG hex data
    function isValidLoginKeySignature(
        bytes memory _msg,
        bytes memory _signatures
    )
        public
        view
        returns (bytes4)
    {
        bytes memory msgHashSignature = _signatures.slice(0, 65);
        bytes memory loginKeyAttestationSignature = _signatures.slice(65, 65);

        address _loginKeyAddress = _getEthSignedMessageHash(_msg).recover(
            msgHashSignature
        );

        // NOTE: The OpenZeppelin toEthSignedMessageHash is used here (and not above)
        // NOTE: because the length is hard coded at 32 and we know that this will always
        // NOTE: be true for this line.
        bytes32 loginKeyAttestationMessageHash = keccak256(abi.encodePacked(
            _loginKeyAddress
        )).toEthSignedMessageHash();

        address _authKeyAddress = loginKeyAttestationMessageHash.recover(
            loginKeyAttestationSignature
        );

        bytes4 sig;
        _isValidAuthKey(_authKeyAddress) ? sig = VALID_SIG : sig = INVALID_SIG;
        return sig;
    }

    /**
     *  Internal functions
     */

    /// @dev Adds ETH signed message prefix to bytes message and hashes it
    /// @param _msg Bytes message before adding the prefix
    /// @return Prefixed and hashed message
    function _getEthSignedMessageHash(bytes memory _msg) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", _uint2str(_msg.length), _msg));
    }

    /// @dev Convert uint to string
    /// @param _num Uint to be converted
    /// @return String equivalent of the uint
    function _uint2str(uint _num) private pure returns (string memory _uintAsString) {
        if (_num == 0) {
            return "0";
        }
        uint i = _num;
        uint j = _num;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len - 1;
        while (i != 0) {
            bstr[k--] = byte(uint8(48 + i % 10));
            i /= 10;
        }
        return string(bstr);
    }
}
function isValidSignature(
    bytes memory _msg,
    bytes memory _signatures
)
    public
    view
    returns (bytes4)
{
    if (_signatures.length == 65) {
        return isValidAuthKeySignature(_msg, _signatures);
    } else if (_signatures.length == 130) {
        return isValidLoginKeySignature(_msg, _signatures);
    } else {
        revert("Invalid _signatures length");
    }
}

/// @dev Check if a message and signature pair is valid
/// @notice The _signatures parameter can either be one auth key signature or it can
/// @notice be a login key signature and an auth key signature (signed login key)
/// @param _msg Message that was signed
/// @param _signatures Signature(s) of the data. Either a single signature (login) or two (login and auth)
/// @return VALID_SIG or INVALID_SIG hex data